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
Version: $Revision$
Last-Modified: $Date$
Author: Kevin D. Smith <Kevin.Smith@theMorgue.org>,
Jim Jewett <jimjjewett@users.sourceforge.net>,
Skip Montanaro <skip@pobox.com>,
Anthony Baxter <anthony@python.org>
Author: Kevin D. Smith, Jim Jewett, Skip Montanaro, Anthony Baxter
Status: Draft
Type: Standards Track
Content-Type: text/x-rst
Created: 05-Jun-2003
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
=====================
This is not yet complete. This is still a work-in-progress. Feedback
to anthony. Please note that the point of this PEP is *not* to
provide an open-slather of plusses and minuses for each syntax, but is
instead to justify the choice made. If, in 2.4a3, the syntax is
changed, the PEP will be updated to match this, complete with the
arguments for the change.
The final decision on the syntax for 2.4a3 is not yet made. This
will be done before 2.4a3, and this document will be updated to
match. Note also that this document does not attempt to cover the
huge number of potential alternative syntaxes, nor is it an attempt
to exhaustively list all the positives and negatives of each form.
Abstract
========
@ -112,6 +107,10 @@ few problems seem to be most problematic.
decorator concept before encountering it in Python. There's just no
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
==========
@ -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
worthwhile to break the syntax discussion down into a number of areas.
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:
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,
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
chances of a large number of decorators being used on a single function
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
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
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):
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:
- 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 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
The next form is that the decorator syntax go inside the method
@ -399,26 +405,70 @@ start three indent levels in.
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
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
that it does not break IPython and Leo. Its major disadvantage
compared to the @syntax is that the | symbol looks like both a
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
meaningful (when used in the form before the method). It's also
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()``
@ -427,116 +477,7 @@ Syntax forms
following function. Both Jp Calderone and Philip Eby produced
implementations of functions that did this. Guido was pretty firmly
against this -- with no new syntax, the magicness of a function like
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:
this is extremely high:
Using functions with "action-at-a-distance" through
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
can be implemented in 2.3.
A `page on the Python Wiki`_ was created to summarize a number of the
proposals. Once it stabilizes perhaps someone would care to
incorporate its content into this PEP (hint, hint).
* new keyword (and block)
.. _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
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
appears) to specify the decorators a few alternatives were proposed:
``[|...|]``, ``*[...]*``, and ``<...>``. None 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.
``[|...|]``, ``*[...]*``, and ``<...>``.
.. _Javadoc comments:
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
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
for 2.4a2 is::
@ -610,6 +561,24 @@ list-after-def syntax is also still kicking around.
.. _patch: http://www.python.org/sf/979728
.. _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
========
@ -632,6 +601,9 @@ some examples of use.
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
disappears enterprising programmers would have to be more creative
to create more instances. (From Shane Hathaway on ``python-dev``.)
@ -664,9 +636,9 @@ some examples of use.
def mymethod(f):
...
4. Enforce function argument and return types. (Note that this is not
exactly correct, as the returned new_f doesn't have "func" as its
func_name attribute.) ::
4. Enforce function argument and return types. Note that this
copies the func_name attribute from the old to the new function.
func_name was made writable in Python 2.4a3::
def accepts(*types):
def check_accepts(f):
@ -676,6 +648,7 @@ some examples of use.
assert isinstance(a, t), \
"arg %r does not match %s" % (a,t)
return f(*args, **kwds)
new_f.func_name = f.func_name
return new_f
return check_accepts
@ -686,6 +659,7 @@ some examples of use.
assert isinstance(result, rtype), \
"return value %r does not match %s" % (result,rtype)
return result
new_f.func_name = f.func_name
return new_f
return check_returns