2003-06-09 00:04:58 -04:00
|
|
|
|
PEP: 318
|
2003-06-10 00:29:48 -04:00
|
|
|
|
Title: Function/Method Decorator Syntax
|
2003-06-09 00:04:58 -04:00
|
|
|
|
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
|
2004-02-28 14:09:44 -05:00
|
|
|
|
Post-History: 09-Jun-2003, 10-Jun-2003, 27-Feb-2004
|
2003-06-09 00:04:58 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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.
|
2003-06-10 00:29:48 -04:00
|
|
|
|
The proposed syntax, shown in the example below, is discussed
|
|
|
|
|
in the following sections.
|
|
|
|
|
|
|
|
|
|
def foo(self) as synchronized(lock), classmethod:
|
|
|
|
|
perform method operation
|
2003-06-09 00:04:58 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Proposal
|
|
|
|
|
|
2003-06-10 00:29:48 -04:00
|
|
|
|
Probably the simplest way to place the decorator that translates
|
2003-06-09 00:04:58 -04:00
|
|
|
|
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
|
2003-06-10 00:29:48 -04:00
|
|
|
|
decorators in the form of a space delimited list as follows:
|
2003-06-09 00:04:58 -04:00
|
|
|
|
|
|
|
|
|
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
|
2003-06-10 00:29:48 -04:00
|
|
|
|
sent to the function decorator.
|
2003-06-09 00:04:58 -04:00
|
|
|
|
|
2003-06-10 00:29:48 -04:00
|
|
|
|
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
|
2003-06-09 00:04:58 -04:00
|
|
|
|
|
2003-06-10 00:29:48 -04:00
|
|
|
|
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,
|
2004-02-28 14:09:44 -05:00
|
|
|
|
or a tuple. Using the latter form, the last example above
|
|
|
|
|
would look like:
|
2003-06-10 00:29:48 -04:00
|
|
|
|
|
2004-02-28 14:09:44 -05:00
|
|
|
|
def foo(self) as (synchronized(lock), classmethod):
|
|
|
|
|
perform method operation
|
|
|
|
|
|
|
|
|
|
This form make is possible for the list of decorators to
|
|
|
|
|
span multiple lines without using the line continuation operator.
|
|
|
|
|
|
|
|
|
|
Alternate Syntaxes
|
|
|
|
|
|
|
|
|
|
Other syntaxes have been proposed in comp.lang.python and
|
|
|
|
|
python-dev. Unfortunately, no one syntax has come out as a clear
|
|
|
|
|
winner in the lengthy discussions. The most common suggestions
|
|
|
|
|
are demonstrated below. The proposed syntax is also included
|
|
|
|
|
for easy comparison.
|
|
|
|
|
|
|
|
|
|
Proposed Syntax
|
|
|
|
|
|
|
|
|
|
def foo(self) as synchronized(lock), classmethod:
|
|
|
|
|
perform method operation
|
|
|
|
|
|
|
|
|
|
def foo(self) as (synchronized(lock), classmethod):
|
|
|
|
|
perform method operation
|
|
|
|
|
|
|
|
|
|
Prefix Forms
|
|
|
|
|
|
|
|
|
|
def [synchronized(lock), classmethod] foo(self):
|
|
|
|
|
perform method operation
|
|
|
|
|
|
|
|
|
|
def synchronized(lock), classmethod foo(self):
|
|
|
|
|
perform method operation
|
|
|
|
|
|
|
|
|
|
# Same as above, but only identifiers are allowed
|
|
|
|
|
sync = synchronized(lock)
|
|
|
|
|
def sync, classmethod foo(self):
|
|
|
|
|
perform method operation
|
|
|
|
|
|
|
|
|
|
# Java-like
|
|
|
|
|
sync = synchronized(lock)
|
|
|
|
|
def @sync @classmethod foo(self):
|
|
|
|
|
perform method operation
|
|
|
|
|
|
|
|
|
|
Postfix Forms
|
2003-06-09 00:04:58 -04:00
|
|
|
|
|
|
|
|
|
def foo(self) [synchronized(lock), classmethod]:
|
|
|
|
|
perform method operation
|
|
|
|
|
|
2003-06-10 00:29:48 -04:00
|
|
|
|
def foo(self) (synchronized(lock), classmethod):
|
|
|
|
|
perform method operation
|
|
|
|
|
|
2003-06-09 00:04:58 -04:00
|
|
|
|
def foo(self) {'pre': synchronized(lock), 'classmethod': True}:
|
|
|
|
|
perform method operation
|
|
|
|
|
|
2004-02-28 14:09:44 -05:00
|
|
|
|
I'm not as fond of the forms that use '[ ]' since code like
|
|
|
|
|
'foo()[a]' looks as if you are getting the item 'a' from 'foo()'.
|
|
|
|
|
Although, this isn't as much of an issue when using '[ ]' in
|
|
|
|
|
a prefix form. The Java-like syntax adds new syntax that is
|
|
|
|
|
very arbitrary and is almost Perl-ish. In addition, since the
|
|
|
|
|
order in which the decorators are applied may matter, the last,
|
|
|
|
|
dictionary-style, syntax must be eliminated.
|
2003-06-10 00:29:48 -04:00
|
|
|
|
|
|
|
|
|
Implementation Issues
|
|
|
|
|
|
|
|
|
|
In the following example there are two function decorators:
|
|
|
|
|
synchronized(lock) and classmethod.
|
|
|
|
|
|
2003-06-09 00:04:58 -04:00
|
|
|
|
def foo(self) as synchronized(lock), classmethod:
|
|
|
|
|
perform method operation
|
|
|
|
|
|
2003-06-10 00:29:48 -04:00
|
|
|
|
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
|
2003-07-29 11:31:13 -04:00
|
|
|
|
is executed. In addition, each of these arguments will be
|
2003-06-10 00:29:48 -04:00
|
|
|
|
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
|
2003-07-29 11:31:13 -04:00
|
|
|
|
name when the 'def' statement is executed.
|
|
|
|
|
|
2004-02-28 14:09:44 -05:00
|
|
|
|
Open Issues
|
|
|
|
|
|
|
|
|
|
It is not clear at the moment if it is even possible to have
|
|
|
|
|
multiple decorators for a function. If decorators are required
|
|
|
|
|
to take a function/method and return a descriptor, it might
|
|
|
|
|
not even be possible to wrap multiple decorators. This should
|
|
|
|
|
be explored since the best syntax for multiple decorators
|
|
|
|
|
may not be the same as the best syntax for a single decorator.
|
2003-07-29 11:31:13 -04:00
|
|
|
|
|
|
|
|
|
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.
|
2003-06-10 00:29:48 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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().
|
2003-06-09 00:04:58 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Conclusion
|
|
|
|
|
|
|
|
|
|
The current method of translating an instance method to a class
|
|
|
|
|
or static method is awkward. A new syntax for applying function
|
2003-06-10 00:29:48 -04:00
|
|
|
|
decorators should be implemented (proposed syntax shown below).
|
2003-06-09 00:04:58 -04:00
|
|
|
|
|
|
|
|
|
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:
|