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
|
||||
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
|
||||
|
||||
|
|
Loading…
Reference in New Issue