I'm done with this now. It'll need a further update when Guido makes a

decision.
This commit is contained in:
Anthony Baxter 2004-08-30 13:16:56 +00:00
parent 93eddee292
commit 4500941247
1 changed files with 123 additions and 149 deletions

View File

@ -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