PEP: 318 Title: Function/Method Decorator Syntax Version: $Revision$ Last-Modified: $Date$ Author: Kevin D. Smith Status: Draft Type: Standards Track Content-Type: text/plain Created: 05-Jun-2003 Python-Version: 2.4 Post-History: 09-Jun-2003, 10-Jun-2003 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 executed. 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((foo)) In the example above, 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 = (classmethod(foo)) In either case, the modified function is bound to the function name when the 'def' statement is executed. Current Implementations I am not personally familiar enough with Python's source to implement the proposed syntax; however, Michael Hudson has implemented the "square-bracketed" syntax (see patch at http://starship.python.net/crew/mwh/hacks/meth-syntax-sugar.diff). It should be fairly simple for the Python development team to translate this patch to the proposed syntax. 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. Local Variables: mode: indented-text indent-tabs-mode: nil sentence-end-double-space: t fill-column: 70 End: