PEP 318 - Function Modifier Syntax

Bengt Richter bokr at oz.net
Tue Jun 10 09:28:41 CEST 2003


On 9 Jun 2003 12:16:16 GMT, Kevin Smith <Kevin.Smith at sas.com> wrote:

>This is the first draft of a PEP describing new syntax for applying 
>function modifiers (e.g. classmethod, staticmethod, etc).  There is 
>currently no implementation of the proposed syntax (I have heard rumors 
>that the 'def foo(self) [...]' syntax has a patch somewhere, but I 
>haven't seen it yet).  I have already received a few comments and will 
>revise the document soon.  The latest version will always be available 
>at http://www.python.org/peps/pep-0318.html.
>
>
>Function Modifier Syntax
>------------------------
>
>Abstract
>    
>    The current method for declaring class and static methods
>    is awkward and can lead to code that is difficult to understand.
>    This PEP introduces possible new syntax which will place the
>    translation of instance methods to class/static methods at
>    the same point in the code as the method's declaration. 
>
>
>Motivation
>
>    The current method of translating an instance method into a 
>    class/static method places the actual translation at a different
>    point in the code than the declaration of the method.  The 
>    code below demonstrates this.
>
>        def foo(self):
>            perform method operation
>        foo = classmethod(foo)
>
>    When the method is very short, it is easy to look ahead and see
>    that this is a class method.  However, if the method is more than
>    15 lines or so, the translation into a class method is not 
>    obvious.  A solution to this problem is to move the translation
>    of the method to the same point as the method's declaration.
>
>
>Proposal
>
>    Probably the simplest way to place the function that translates
>    an instance method to a class/static method is illustrated in the
>    code below.
>
>        def classmethod foo(self):
>            perform method operation 
>
>    The code in this example will simply perform the following.
>
>        def foo(self):
>            perform method operation
>        foo = classmethod(foo)
>     
>    This syntax does not introduce any new keywords and is completely
>    backwards compatible with any existing code.  The word between the
>    'def' and the actual name of the method is simply a reference to
>    a callable object that returns a new function reference. 
>    This syntax could also be extended to allow multiple function 
>    modifiers in the form of a space delimited list as follows:
>
>       def protected classmethod foo(self):
>           perform method operation 
>
>    which would be equivalent to the current form:
>
>       def foo(self):
>           perform method operation
>       foo = protected(classmethod(foo))
>
>    While this syntax is simple and easy to read, it does become 
>    cluttered and more obscure if you wish to allow arguments to be 
>    sent to the function modifier.
>
>       def synchronized(lock) classmethod foo(self):
>           perform method operation 
>
>    Various syntaxes have been proposed in comp.lang.python.  The
>    most common are demonstrated below.
>
>        def foo(self) [synchronized(lock), classmethod]:
>            perform method operation
>
>        def foo(self) {'pre': synchronized(lock), 'classmethod': True}:
>            """ Skip Montanaro syntax """
>            perform method operation
I like this one, except any dict workalike expression in the position
between "foo(self)" and ":" ought to work. E.g.,

         MOD_SYN_METH = {'pre': synchronized(lock), 'classmethod': True}
         def foo(self) MOD_SYN_METH:
             """ Skip Montanaro syntax """
             perform method operation

ought to work the same. But there has to be some control definition for how/when
to use the things in the dict (noting that there is at least def-time vs call time).
I would designate an optional overriding  mod function right in the dict itself
using a special key, e.g., 'mod_fun'

         MOD_SYN_METH = {'mod_fun': mod_syn, 'pre': synchronized(lock), 'classmethod': True}
         def foo(self) MOD_SYN_METH:
             """ Skip Montanaro syntax with Bengt's mod """
             perform method operation

The implied processing might be

         foo = MOD_SYN_METH.get('mod_fun', std_mod_fun)(foo, MOD_SYN_METH, locs=locals(), globs=globals())

where std_mod_fun would be a builtin default. This would provide a more open-ended facility for
modding, e.g.,
      
         def foogen(arg,arg1,...) {'mod_fun'= std_mod_gen}:

with the concise spelling using a builtin dict perhaps being

         def foogen(arg,arg1,...) GENERATOR:  # COROUTINE: springs to mind also

might be a possible alternative way to make a function into a generator without scanning for yields
(which in turn might open up the possibility of a yield from nested or other called functions, BTW).

One could imagine extending this pattern to classes also, e.g.,

         class Foo(B1, B2) {'mod_fun': __METACLASS__}
             """ Skip Montanaro syntax with Bengt's mod applied to classes """
             perform class definition

Hm, to round this out, maybe a class mod that turns the class into a first class module in
dotted sub-relation to the module containing it. Thus

         class MyModule(bases) MODULE:
             """ Skip Montanaro syntax with Bengt's mod applied to classes, module in particular """
             perform module definition

The standard MODULE['mod_fun'] would perhaps (handwaving ;-) produce a singleton module instance
and bind it to MyModule, but there would be other possibilities following from the general mechanism,
perhaps including properties for the module.
    
>
>        def foo(self) as synchronized(lock), classmethod:
>            """ Gerrit Holl syntax """
>            perform method operation

Ok, time to mention a question about "synchronized(lock)" -- what does it mean? I.e., is it an
expression evaluated at def time? BTW this question also affects Skip's modding dictionary (with
or without my extensions). Is the intent really {'pre':(synchronized,lock)} so that the function
can be set up to make the call MODDICT['pre'][0](MODDICT['pre'][1]) at run time (though not looking
those up dynamically in MODDICT at run time), as opposed to using a result from the def-time
call "synchronized(lock)"?

BTW, local presets for a function could be handled at def time by an element in the mod dict, e.g.,

         def foo(x) {'mod_fun':std_mod_fun, 'fun_locals':{'a':123, 'count':0, 'shared_mutable':[]}:
             x = a  # would get the 123 constant local value "a" preset-each-time-foo-is-called, not global
             count +=1 # would always have the preset starting value

This could be made more concise (and flexible) if multiple dictionaries meant that they should all be
in effect searched in reverse order for a name in mod dict space. E.g.,

         def foo(x) FUNCTION, {fun_locals':{'a':123, 'count':0, 'shared_mutable':[]}:

where FUNCTION would be the default builtin used when you write plain old "def foo(x):"
Of course, a class instance with __GETITEM__ defined could allow you to spell that as

         def foo(x) FUN_PRESETS(a=123, count=0, shared_mutable=[]):

Maybe a naming convention is needed to reserve the standard modding dicts, e.g.,

         def foo(self) MOD_CLASSMETHOD: pass
         def foo(self) MOD_STATICMETHOD: pass
         def foo(self) MOD_STD_METHOD: pass # default within class def
         class foo(opt_bases) MOD_PROPERTY(get=False, set=True) # instance acts like dict
             # expect only set accessor method here, since defaults for readonly are overridden
         def foo(some, args) MOD_GENERATOR: pass

and so forth...        
>
>    I have a strong preference for the last of the three.  The first
>    two use syntax that just seems arbitrary which does not help the
>    user to understand the meaning of it.  The third method is very
>    readable and could probably be interpreted easily by those not
>    familiar with Python.
I like to have a concise spelling for things, but I like it to be the
simple designation for something that can also be elaborated and customized elsewhere.
>
>
>Conclusion
>
>    The current method of translating an instance method to a class
>    or static method is awkward.  A new syntax for applying function
>    modifiers should be implemented (proposed syntax shown below).  
>
>        def foo(self) as synchronized(lock), classmethod:
>            perform method operation
>
>    More generally, 
>
>        def foo(self) as <tuple>:
>            perform method operation
>
As indicated above, I like the flixibility of

         def foo(self) <mod dict expression>:
             perform method operation

Though if someone wants to interpose a sugary 'as' I wouldn't get apoplectic ;-)

>    The proposed syntax is simple, powerful, easy to read, and 
>    therefore preserves those qualities of the Python language.

I think this applies even more when standard patterns can be reduced to a single symbol,
while preserving customizability, e.g.,

         def foo(self) as MY_LOCKED_METHOD:
             perform method operation

BTW, I think it's good to keep in mind that "*perform* method operation" refers to call/run-time,
not def-time, where the meta-magic is being done to set up for later run time. Of course, the
meta-magic has its own def- and run- times ;-)

>
>
>Copyright
>
>    This document has been placed in the public domain.
>
>
>-- 
>Kevin Smith
>Kevin.Smith at sas.com

Above comments are version 0.1, so I expect some holes, but HTH ;-)

Regards,
Bengt Richter




More information about the Python-list mailing list