Revised PEP 318 - Function/Method Decorator Syntax

Kevin Smith Kevin.Smith at sas.com
Tue Jun 10 08:25:13 EDT 2003


I have received many comments on this proposal already and have taken 
them into account in this proposal.  The proposed syntax is still 
essentially the same, but more implementation details have been added 
and the document has been restructured somewhat for clarity.  There are 
still some legitimate concerns about using 'as' for the delimiter as it 
means something quite different in the import statement.  Suggestions 
for a new delimiter are welcome. 


Function/Method Decorator 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.
    The proposed syntax, shown in the example below, is discussed 
    in the following sections.

        def foo(self) as synchronized(lock), classmethod:
            perform method operation


Proposal

    Probably the simplest way to place the decorator 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 
    decorators 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 decorator.

        def synchronized(lock) classmethod foo(self):
            perform method operation 

    Instead of placing the decorators in front of the function name,
    a better place might be after it, as shown below.  The word 'as' is 
    added simply as a separator to assist in readability.  

        def foo(self) as synchronized(lock), classmethod:
            perform method operation

    This syntax is quite clear and could probably be interpreted 
    by those not familiar with Python.  The proposed syntax can be
    generalized as follows: 

        'def' NAME '(' PARAMETERS ')' ['as' DECORATORS] ':'

    where DECORATORS is a comma-separated list of expressions, 
    or a tuple.

    Other 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) (synchronized(lock), classmethod):
            perform method operation

        def foo(self) {'pre': synchronized(lock), 'classmethod': True}:
            perform method operation

    These three forms use syntax that just seems arbitrary and which 
    does not help the user to comprehend the meaning of it.  In 
    addition, since the order in which the decorators are applied 
    may matter, the third, dictionary-style, syntax must be 
    eliminated.  

    
Implementation Issues

    In the following example there are two function decorators: 
    synchronized(lock) and classmethod.  

        def foo(self) as synchronized(lock), classmethod:
            perform method operation

    Since these all appear within the operation of the 'def' 
    itself, it makes sense that synchronized, lock, and 
    classmethod must exist at the time that the definition 
    is compiled.  In addition, each of these arguments will be 
    evaluated before being applied to the compiled function.  
    This means that arguments like synchronized(lock) must 
    return a descriptor that will be applied to foo.  Therefore, 
    the code above translates to:

        def foo(self):
            perform method operation
        foo = classmethod(<returned-descriptor>(foo))
    
    In the example above, <returned-descriptor> refers to the 
    descriptor returned by evaluating synchronized(lock).

    It could easily be argued that the descriptors should be applied
    in reverse order to make the application of the descriptor look 
    more like the resultant code.  I tend to prefer this form.

        def foo(self):
            perform method operation
        foo = <returned-descriptor>(classmethod(foo))

    In either case, the modified function is bound to the function
    name at compile time.


Possible Extensions

    The proposed syntax is general enough that it could be used 
    on class definitions as well as shown below.

        class foo(object) as classmodifier:
            class definition here

    However, there are no obvious parallels for use with other
    descriptors such as property().


Conclusion

    The current method of translating an instance method to a class
    or static method is awkward.  A new syntax for applying function
    decorators should be implemented (proposed syntax shown below).  

        def foo(self) as synchronized(lock), classmethod:
            perform method operation

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


Copyright

    This document has been placed in the public domain.


-- 
Kevin Smith
Kevin.Smith at sas.com




More information about the Python-list mailing list