202 lines
6.6 KiB
Plaintext
202 lines
6.6 KiB
Plaintext
PEP: 318
|
||
Title: Function/Method Decorator Syntax
|
||
Version: $Revision$
|
||
Last-Modified: $Date$
|
||
Author: Kevin D. Smith <Kevin.Smith@theMorgue.org>
|
||
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(<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 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:
|