I'm done with this now. It'll need a further update when Guido makes a
decision.
This commit is contained in:
parent
93eddee292
commit
4500941247
272
pep-0318.txt
272
pep-0318.txt
|
@ -2,28 +2,23 @@ PEP: 318
|
||||||
Title: Decorators for Functions and Methods
|
Title: Decorators for Functions and Methods
|
||||||
Version: $Revision$
|
Version: $Revision$
|
||||||
Last-Modified: $Date$
|
Last-Modified: $Date$
|
||||||
Author: Kevin D. Smith <Kevin.Smith@theMorgue.org>,
|
Author: Kevin D. Smith, Jim Jewett, Skip Montanaro, Anthony Baxter
|
||||||
Jim Jewett <jimjjewett@users.sourceforge.net>,
|
|
||||||
Skip Montanaro <skip@pobox.com>,
|
|
||||||
Anthony Baxter <anthony@python.org>
|
|
||||||
Status: Draft
|
Status: Draft
|
||||||
Type: Standards Track
|
Type: Standards Track
|
||||||
Content-Type: text/x-rst
|
Content-Type: text/x-rst
|
||||||
Created: 05-Jun-2003
|
Created: 05-Jun-2003
|
||||||
Python-Version: 2.4
|
Python-Version: 2.4
|
||||||
Post-History: 09-Jun-2003, 10-Jun-2003, 27-Feb-2004, 23-Mar-2004
|
Post-History: 09-Jun-2003, 10-Jun-2003, 27-Feb-2004, 23-Mar-2004, 30-Aug-2004
|
||||||
|
|
||||||
|
|
||||||
WarningWarningWarning
|
WarningWarningWarning
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
This is not yet complete. This is still a work-in-progress. Feedback
|
The final decision on the syntax for 2.4a3 is not yet made. This
|
||||||
to anthony. Please note that the point of this PEP is *not* to
|
will be done before 2.4a3, and this document will be updated to
|
||||||
provide an open-slather of plusses and minuses for each syntax, but is
|
match. Note also that this document does not attempt to cover the
|
||||||
instead to justify the choice made. If, in 2.4a3, the syntax is
|
huge number of potential alternative syntaxes, nor is it an attempt
|
||||||
changed, the PEP will be updated to match this, complete with the
|
to exhaustively list all the positives and negatives of each form.
|
||||||
arguments for the change.
|
|
||||||
|
|
||||||
|
|
||||||
Abstract
|
Abstract
|
||||||
========
|
========
|
||||||
|
@ -112,6 +107,10 @@ few problems seem to be most problematic.
|
||||||
decorator concept before encountering it in Python. There's just no
|
decorator concept before encountering it in Python. There's just no
|
||||||
strong preexisting meme that captures the concept.
|
strong preexisting meme that captures the concept.
|
||||||
|
|
||||||
|
* Syntax discussions in general appear to cause more contention than
|
||||||
|
almost anything else. Readers are pointed to the ternary operator
|
||||||
|
discussions that were associated with PEP 308 for another example of
|
||||||
|
this.
|
||||||
|
|
||||||
Background
|
Background
|
||||||
==========
|
==========
|
||||||
|
@ -263,7 +262,7 @@ There have been `a large number`_ of different syntaxes proposed --
|
||||||
rather than attempting to work through these individual syntaxes, it's
|
rather than attempting to work through these individual syntaxes, it's
|
||||||
worthwhile to break the syntax discussion down into a number of areas.
|
worthwhile to break the syntax discussion down into a number of areas.
|
||||||
Attempting to discuss `each possible syntax`_ individually would be an
|
Attempting to discuss `each possible syntax`_ individually would be an
|
||||||
act of madness, and produce a completely unwieldly PEP.
|
act of madness, and produce a completely unwieldy PEP.
|
||||||
|
|
||||||
.. _a large number:
|
.. _a large number:
|
||||||
http://www.python.org/moin/PythonDecorators
|
http://www.python.org/moin/PythonDecorators
|
||||||
|
@ -295,7 +294,7 @@ line of code has a result on a following line. The syntax that
|
||||||
will be in 2.4a3 will also require one decorator per line (in a2,
|
will be in 2.4a3 will also require one decorator per line (in a2,
|
||||||
multiple decorators can be specified on the same line).
|
multiple decorators can be specified on the same line).
|
||||||
|
|
||||||
People also complained that the syntax got unwieldly quickly when
|
People also complained that the syntax got unworldly quickly when
|
||||||
multiple decorators were used. The point was made, though, that the
|
multiple decorators were used. The point was made, though, that the
|
||||||
chances of a large number of decorators being used on a single function
|
chances of a large number of decorators being used on a single function
|
||||||
were small and thus this was not a large worry.
|
were small and thus this was not a large worry.
|
||||||
|
@ -310,6 +309,13 @@ code itself, thus knowing how to interpret the code's semantics
|
||||||
properly without having to go back and change your initial perceptions
|
properly without having to go back and change your initial perceptions
|
||||||
if the syntax did not come before the function definition.
|
if the syntax did not come before the function definition.
|
||||||
|
|
||||||
|
Guido decided `he preferred`_ having the decorators on the line before
|
||||||
|
the 'def', because it was felt that a long argument list would mean
|
||||||
|
that the decorators would be 'hidden'
|
||||||
|
|
||||||
|
.. _he preferred:
|
||||||
|
http://mail.python.org/pipermail/python-dev/2004-March/043756.html
|
||||||
|
|
||||||
The second form is the decorators between the def and the function
|
The second form is the decorators between the def and the function
|
||||||
name, or the function name and the argument list::
|
name, or the function name and the argument list::
|
||||||
|
|
||||||
|
@ -341,7 +347,7 @@ in the 'def' line::
|
||||||
def bar(low,high) @accepts(int,int),@returns(float):
|
def bar(low,high) @accepts(int,int),@returns(float):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
Guido `summarised the arguments`_ against this form (many of which
|
Guido `summarized the arguments`_ against this form (many of which
|
||||||
also apply to the previous form) as:
|
also apply to the previous form) as:
|
||||||
|
|
||||||
- it hides crucial information (e.g. that it is a static method)
|
- it hides crucial information (e.g. that it is a static method)
|
||||||
|
@ -353,7 +359,7 @@ also apply to the previous form) as:
|
||||||
- it's cumbersome to cut and paste a decorator list for reuse, because
|
- it's cumbersome to cut and paste a decorator list for reuse, because
|
||||||
it starts and ends in the middle of a line
|
it starts and ends in the middle of a line
|
||||||
|
|
||||||
.. _summarised the arguments:
|
.. _summarized the arguments:
|
||||||
http://mail.python.org/pipermail/python-dev/2004-August/047112.html
|
http://mail.python.org/pipermail/python-dev/2004-August/047112.html
|
||||||
|
|
||||||
The next form is that the decorator syntax go inside the method
|
The next form is that the decorator syntax go inside the method
|
||||||
|
@ -399,26 +405,70 @@ start three indent levels in.
|
||||||
Syntax forms
|
Syntax forms
|
||||||
------------
|
------------
|
||||||
|
|
||||||
* ``@decorator``
|
* ``@decorator``::
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def foo(arg1,arg2):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@accepts(int,int)
|
||||||
|
@returns(float)
|
||||||
|
def bar(low,high):
|
||||||
|
pass
|
||||||
|
|
||||||
The major objections against this syntax are that the @ symbol is
|
The major objections against this syntax are that the @ symbol is
|
||||||
not currently used in Python (and is used in both IPython and Leo),
|
not currently used in Python (and is used in both IPython and Leo),
|
||||||
that the @ symbol is not meaningful,
|
and that the @ symbol is not meaningful. Another objection is that
|
||||||
|
this "wastes" a currently unused character (from a limited set) on
|
||||||
|
something that is not perceived as a major use.
|
||||||
|
|
||||||
* ``|decorator``
|
* ``|decorator``::
|
||||||
|
|
||||||
|
|classmethod
|
||||||
|
def foo(arg1,arg2):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|accepts(int,int)
|
||||||
|
|returns(float)
|
||||||
|
def bar(low,high):
|
||||||
|
pass
|
||||||
|
|
||||||
This is a variant on the @decorator syntax -- it has the advantage
|
This is a variant on the @decorator syntax -- it has the advantage
|
||||||
that it does not break IPython and Leo. Its major disadvantage
|
that it does not break IPython and Leo. Its major disadvantage
|
||||||
compared to the @syntax is that the | symbol looks like both a
|
compared to the @syntax is that the | symbol looks like both a
|
||||||
capital I and a lowercase l.
|
capital I and a lowercase l.
|
||||||
|
|
||||||
* list syntax
|
* list syntax::
|
||||||
|
|
||||||
|
[classmethod]
|
||||||
|
def foo(arg1,arg2):
|
||||||
|
pass
|
||||||
|
|
||||||
|
[accepts(int,int), returns(float)]
|
||||||
|
def bar(low,high):
|
||||||
|
pass
|
||||||
|
|
||||||
The major objection to the list syntax is that it's currently
|
The major objection to the list syntax is that it's currently
|
||||||
meaningful (when used in the form before the method). It's also
|
meaningful (when used in the form before the method). It's also
|
||||||
lacking any indication that the expression is a decorator.
|
lacking any indication that the expression is a decorator.
|
||||||
|
|
||||||
* list syntax using other brackets (``<...>``, ``[[...]]``, ...)
|
* list syntax using other brackets (``<...>``, ``[[...]]``, ...)::
|
||||||
|
|
||||||
|
<classmethod>
|
||||||
|
def foo(arg1,arg2):
|
||||||
|
pass
|
||||||
|
|
||||||
|
<accepts(int,int), returns(float)>
|
||||||
|
def bar(low,high):
|
||||||
|
pass
|
||||||
|
|
||||||
|
None of these alternatives gained much traction. The alternatives
|
||||||
|
which involve square brackets only serve to make it obvious that the
|
||||||
|
decorator construct is not a list. They do nothing to make parsing any
|
||||||
|
easier. The '<...>' alternative presents parsing problems because '<'
|
||||||
|
and '>' already parse as un-paired. They present a further parsing
|
||||||
|
ambiguity because a right angle bracket might be a greater than symbol
|
||||||
|
instead of a closer for the decorators.
|
||||||
|
|
||||||
* ``decorate()``
|
* ``decorate()``
|
||||||
|
|
||||||
|
@ -427,116 +477,7 @@ Syntax forms
|
||||||
following function. Both Jp Calderone and Philip Eby produced
|
following function. Both Jp Calderone and Philip Eby produced
|
||||||
implementations of functions that did this. Guido was pretty firmly
|
implementations of functions that did this. Guido was pretty firmly
|
||||||
against this -- with no new syntax, the magicness of a function like
|
against this -- with no new syntax, the magicness of a function like
|
||||||
this is extremely high.
|
this is extremely high:
|
||||||
|
|
||||||
* new keyword (and block)
|
|
||||||
|
|
||||||
|
|
||||||
Alternate Proposals
|
|
||||||
===================
|
|
||||||
|
|
||||||
The text in this section will be integrated into the previous section.
|
|
||||||
They're here for hysterical raisins for now.
|
|
||||||
|
|
||||||
|
|
||||||
Several other syntaxes have been proposed::
|
|
||||||
|
|
||||||
def func(arg1, arg2, ...) as dec1, dec2, ...:
|
|
||||||
pass
|
|
||||||
|
|
||||||
The absence of brackets makes it cumbersome to break long lists of
|
|
||||||
decorators across multiple lines, and the keyword "as" doesn't have
|
|
||||||
the same meaning as its use in the ``import`` statement. Plenty of
|
|
||||||
`alternatives to "as"`_ have been proposed. :-) ::
|
|
||||||
|
|
||||||
def [dec1, dec2, ...] func(arg1, arg2, ...):
|
|
||||||
pass
|
|
||||||
|
|
||||||
.. _alternatives to "as":
|
|
||||||
http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&threadm=mailman.236.1079968472.742.python-list%40python.org&rnum=2&prev=/groups%3Fq%3Dpython%2Bpep%2B318%26hl%3Den%26lr%3D%26ie%3DUTF-8%26oe%3DUTF-8%26selm%3Dmailman.236.1079968472.742.python-list%2540python.org%26rnum%3D2
|
|
||||||
|
|
||||||
This form has the disadvantage that the decorators visually assume
|
|
||||||
higher priority than the function name and argument list. ::
|
|
||||||
|
|
||||||
def func [dec1, dec2, ...] (arg1, arg2, ...):
|
|
||||||
pass
|
|
||||||
|
|
||||||
Quixote's `Python Template Language`_ uses this form, but only
|
|
||||||
supports a single decorator chosen from a restricted set. For short
|
|
||||||
lists it works okay, but for long list it separates the argument list
|
|
||||||
from the function name. ::
|
|
||||||
|
|
||||||
using:
|
|
||||||
dec1
|
|
||||||
dec2
|
|
||||||
...
|
|
||||||
def foo(arg1, arg2, ...):
|
|
||||||
pass
|
|
||||||
|
|
||||||
.. _Python Template Language:
|
|
||||||
http://www.mems-exchange.org/software/quixote/doc/PTL.html
|
|
||||||
|
|
||||||
The function definition is not nested within the using: block making
|
|
||||||
it impossible to tell which objects following the block will be
|
|
||||||
decorated. Nesting the function definition within the using: block
|
|
||||||
suggests nesting of namespaces that doesn't exist. The name ``foo``
|
|
||||||
would actually exist at the same scope as the using: block. Finally,
|
|
||||||
it would require the introduction of a new keyword.
|
|
||||||
|
|
||||||
The obvious alternative that nests the function within the block ::
|
|
||||||
|
|
||||||
using:
|
|
||||||
dec1
|
|
||||||
dec2
|
|
||||||
...
|
|
||||||
def foo(arg1, arg2, ...):
|
|
||||||
pass
|
|
||||||
|
|
||||||
has its own set of drawbacks. Having the minimal indent level be
|
|
||||||
three deep for methods is painful for those using limited-width
|
|
||||||
windows. The inconsistent indentation between methods of the same
|
|
||||||
class with and without decorators would be a readability problem.
|
|
||||||
Finally, adding or removing decorators would require reindenting the
|
|
||||||
entire function/method body.
|
|
||||||
|
|
||||||
Guido proposed and implemented a patch to support interpretation of
|
|
||||||
a `list of decorators`_ as a prefix to function definitions ::
|
|
||||||
|
|
||||||
[dec1, dec2, ...]
|
|
||||||
def foo(arg1, arg2, ...):
|
|
||||||
pass
|
|
||||||
|
|
||||||
For a while this was Guido's preferred solution, but negative
|
|
||||||
sentiment ran high, mostly because that syntax, though useless except
|
|
||||||
for side effects of the list, is already legal and thus creates a
|
|
||||||
special case.
|
|
||||||
|
|
||||||
One attraction people later had to the syntax, though, was the
|
|
||||||
possibility of implementing a backwards-compatible implementation of
|
|
||||||
decorators so that versions prior to 2.4 (back to 2.2) could also use
|
|
||||||
the new syntax. But it was decided this was not a valid argument;
|
|
||||||
Since the feature is new it should not be restricted just so that
|
|
||||||
previous versions could possibly use it.
|
|
||||||
|
|
||||||
.. _list of decorators:
|
|
||||||
http://python.org/sf/926860
|
|
||||||
|
|
||||||
Another variant on the list syntax that was initially favored was::
|
|
||||||
|
|
||||||
def func(arg1, arg2, ...) [dec1, dec2]:
|
|
||||||
pass
|
|
||||||
|
|
||||||
Guido decided `he preferred`_ having the decorators on the line before
|
|
||||||
the 'def', because it was felt that a long argument list would mean
|
|
||||||
that the decorators would be 'hidden'
|
|
||||||
|
|
||||||
.. _he preferred:
|
|
||||||
http://mail.python.org/pipermail/python-dev/2004-March/043756.html
|
|
||||||
|
|
||||||
Phillip Eby and Jp Calderone both proposed variants that required
|
|
||||||
no new syntax, but instead used some fairly advanced introspection
|
|
||||||
to provide decorator-like behavoiur, but Guido was unimpressed by
|
|
||||||
these, stating:
|
|
||||||
|
|
||||||
Using functions with "action-at-a-distance" through
|
Using functions with "action-at-a-distance" through
|
||||||
sys.settraceback may be okay for an obscure feature that can't be
|
sys.settraceback may be okay for an obscure feature that can't be
|
||||||
|
@ -548,14 +489,29 @@ these, stating:
|
||||||
their design needs to be forward-looking, not constrained by what
|
their design needs to be forward-looking, not constrained by what
|
||||||
can be implemented in 2.3.
|
can be implemented in 2.3.
|
||||||
|
|
||||||
A `page on the Python Wiki`_ was created to summarize a number of the
|
* new keyword (and block)
|
||||||
proposals. Once it stabilizes perhaps someone would care to
|
|
||||||
incorporate its content into this PEP (hint, hint).
|
|
||||||
|
|
||||||
.. _page on the Python Wiki:
|
This idea was the consensus alternate from comp.lang.python. Robert
|
||||||
|
Brewer wrote up a detailed `J2 proposal`_ document outlining the arguments
|
||||||
|
in favor of this. The issues with this form are that it requires a new
|
||||||
|
keyword (and therefore a ``from __future__ import decorators`` statement),
|
||||||
|
it seems like there is no obvious choice for the keyword (``using`` is
|
||||||
|
the choice of the proposal and implementation), and that the form
|
||||||
|
produces something that looks like a normal code block, but isn't. Attempts
|
||||||
|
to use statements in this block will cause a syntax error. It's also
|
||||||
|
going to potentially confuse users who don't expect a block to contain
|
||||||
|
bare expressions.
|
||||||
|
|
||||||
|
|
||||||
|
.. _J2 proposal:
|
||||||
|
http://www.aminus.org/rbre/python/pydec.html
|
||||||
|
|
||||||
|
|
||||||
|
There are plenty of other variants and proposals on `the wiki page`_.
|
||||||
|
|
||||||
|
.. _the wiki page:
|
||||||
http://www.python.org/moin/PythonDecorators
|
http://www.python.org/moin/PythonDecorators
|
||||||
|
|
||||||
|
|
||||||
Why @?
|
Why @?
|
||||||
------
|
------
|
||||||
|
|
||||||
|
@ -570,13 +526,8 @@ still a fairly arbitrary choice. Some have suggested using | instead.
|
||||||
|
|
||||||
For syntax options which use a list-like syntax (no matter where it
|
For syntax options which use a list-like syntax (no matter where it
|
||||||
appears) to specify the decorators a few alternatives were proposed:
|
appears) to specify the decorators a few alternatives were proposed:
|
||||||
``[|...|]``, ``*[...]*``, and ``<...>``. None gained much traction.
|
``[|...|]``, ``*[...]*``, and ``<...>``.
|
||||||
The alternatives which involve square brackets only serve to make it
|
|
||||||
obvious that the decorator construct is not a list. They do nothing
|
|
||||||
to make parsing any easier. The '<...>' alternative presents parsing
|
|
||||||
problems because '<' and '>' already parse as un-paired. They present
|
|
||||||
a further parsing ambiguity because a right angle bracket might be a
|
|
||||||
greater than symbol instead of a closer for the decorators.
|
|
||||||
|
|
||||||
.. _Javadoc comments:
|
.. _Javadoc comments:
|
||||||
http://java.sun.com/j2se/javadoc/writingdoccomments/
|
http://java.sun.com/j2se/javadoc/writingdoccomments/
|
||||||
|
@ -584,10 +535,10 @@ greater than symbol instead of a closer for the decorators.
|
||||||
http://java.sun.com/j2se/1.5.0/docs/guide/language/annotations.html
|
http://java.sun.com/j2se/1.5.0/docs/guide/language/annotations.html
|
||||||
|
|
||||||
|
|
||||||
Current Implementation
|
Current Implementation, History
|
||||||
======================
|
===============================
|
||||||
|
|
||||||
Guido asked for a voluteer to implement his preferred syntax, and Mark
|
Guido asked for a volunteer to implement his preferred syntax, and Mark
|
||||||
Russell stepped up and posted a `patch`_ to SF. The syntax accepted
|
Russell stepped up and posted a `patch`_ to SF. The syntax accepted
|
||||||
for 2.4a2 is::
|
for 2.4a2 is::
|
||||||
|
|
||||||
|
@ -610,6 +561,24 @@ list-after-def syntax is also still kicking around.
|
||||||
.. _patch: http://www.python.org/sf/979728
|
.. _patch: http://www.python.org/sf/979728
|
||||||
.. _previous patch: http://starship.python.net/crew/mwh/hacks/meth-syntax-sugar-3.diff
|
.. _previous patch: http://starship.python.net/crew/mwh/hacks/meth-syntax-sugar-3.diff
|
||||||
|
|
||||||
|
After 2.4a2 was released, in response to community reaction, Guido
|
||||||
|
stated that he'd re-examine a community proposal, if the community
|
||||||
|
could come up with a community consensus, a decent proposal, and an
|
||||||
|
implementation.
|
||||||
|
|
||||||
|
After an amazing number of posts, collecting a vast number of alternatives
|
||||||
|
in the `Python wiki`_, the proposed J2 syntax (the new keyword ``using``
|
||||||
|
in a block before the def). Robert Brewer wrote a `detailed proposal`_
|
||||||
|
for this form, and Michael Sparks produced `a patch`_. As at time of
|
||||||
|
writing, we're waiting for Guido's decision.
|
||||||
|
|
||||||
|
.. _Python wiki:
|
||||||
|
http://www.python.org/moin/PythonDecorators
|
||||||
|
.. _detailed proposal:
|
||||||
|
http://www.aminus.org/rbre/python/pydec.html
|
||||||
|
.. _a patch:
|
||||||
|
http://www.python.org/sf/1013835
|
||||||
|
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
========
|
========
|
||||||
|
@ -632,6 +601,9 @@ some examples of use.
|
||||||
def func():
|
def func():
|
||||||
...
|
...
|
||||||
|
|
||||||
|
Note that this example is probably not suitable for real usage, but
|
||||||
|
is for example purposes only.
|
||||||
|
|
||||||
2. Define a class with a singleton instance. Note that once the class
|
2. Define a class with a singleton instance. Note that once the class
|
||||||
disappears enterprising programmers would have to be more creative
|
disappears enterprising programmers would have to be more creative
|
||||||
to create more instances. (From Shane Hathaway on ``python-dev``.)
|
to create more instances. (From Shane Hathaway on ``python-dev``.)
|
||||||
|
@ -664,9 +636,9 @@ some examples of use.
|
||||||
def mymethod(f):
|
def mymethod(f):
|
||||||
...
|
...
|
||||||
|
|
||||||
4. Enforce function argument and return types. (Note that this is not
|
4. Enforce function argument and return types. Note that this
|
||||||
exactly correct, as the returned new_f doesn't have "func" as its
|
copies the func_name attribute from the old to the new function.
|
||||||
func_name attribute.) ::
|
func_name was made writable in Python 2.4a3::
|
||||||
|
|
||||||
def accepts(*types):
|
def accepts(*types):
|
||||||
def check_accepts(f):
|
def check_accepts(f):
|
||||||
|
@ -676,6 +648,7 @@ some examples of use.
|
||||||
assert isinstance(a, t), \
|
assert isinstance(a, t), \
|
||||||
"arg %r does not match %s" % (a,t)
|
"arg %r does not match %s" % (a,t)
|
||||||
return f(*args, **kwds)
|
return f(*args, **kwds)
|
||||||
|
new_f.func_name = f.func_name
|
||||||
return new_f
|
return new_f
|
||||||
return check_accepts
|
return check_accepts
|
||||||
|
|
||||||
|
@ -686,6 +659,7 @@ some examples of use.
|
||||||
assert isinstance(result, rtype), \
|
assert isinstance(result, rtype), \
|
||||||
"return value %r does not match %s" % (result,rtype)
|
"return value %r does not match %s" % (result,rtype)
|
||||||
return result
|
return result
|
||||||
|
new_f.func_name = f.func_name
|
||||||
return new_f
|
return new_f
|
||||||
return check_returns
|
return check_returns
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue