Add verbiage about class decorators and their status

This commit is contained in:
Skip Montanaro 2004-03-25 21:45:58 +00:00
parent 490a2e2fdd
commit c83ec2f982
1 changed files with 83 additions and 39 deletions

View File

@ -1,5 +1,5 @@
PEP: 318
Title: Function/Method Decorator Syntax
Title: Decorators for Functions, Methods and Classes
Version: $Revision$
Last-Modified: $Date$
Author: Kevin D. Smith <Kevin.Smith@theMorgue.org>,
@ -15,11 +15,11 @@ Post-History: 09-Jun-2003, 10-Jun-2003, 27-Feb-2004, 23-Mar-2004
Abstract
========
The current method for declaring class and static methods is awkward
and can lead to code that is difficult to understand. Ideally, these
transformations should be made at the same point in the code where the
declaration itself is made. This PEP introduces new syntax for
The current method for declaring class and static methods is awkward
and can lead to code that is difficult to understand. Ideally, these
transformations should be made at the same point in the code where the
declaration itself is made. This PEP introduces new syntax for
transformations of a declaration.
@ -54,6 +54,13 @@ declaration::
def foo(cls) using [synchronized(lock), classmethod]:
pass
Modifying classes in this fashion is also possible, though the
benefits are not as immediately apparent. Almost certainly, anything
which could be done with class decorators could be done using
metaclasses, but using metaclasses is sufficiently obscure that there
is some attraction to having an easier way to make simple
modifications to classes.
Background
==========
@ -64,8 +71,7 @@ Conference`_, though `he later said`_ it was only one of several
extensions he proposed there "semi-jokingly". `Michael Hudson raised
the topic`_ on ``python-dev`` shortly after the conference,
attributing the bracketed syntax to an earlier proposal on
``comp.lang.python`` by `Gareth
McCaughan`_.
``comp.lang.python`` by `Gareth McCaughan`_.
.. _syntactic support for decorators: http://www.python.org/doc/essays/ppt/python10/py10keynote.pdf
.. _10th python conference: http://www.python.org/workshops/2002-02/
@ -73,6 +79,9 @@ McCaughan`_.
.. _he later said: http://mail.python.org/pipermail/python-dev/2002-February/020017.html
.. _gareth mccaughan: http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=slrna40k88.2h9o.Gareth.McCaughan%40g.local
Class decorations seem like an obvious next step because class
definition and function definition are syntactically similar.
Design Goals
============
@ -83,17 +92,17 @@ The new syntax should
* work with multiple wrappers per definition
* make it obvious what is happening; at the very least it should be
obvious that new users can safely ignore it when writing their own
* make it obvious what is happening; at the very least it should be
obvious that new users can safely ignore it when writing their own
code
* not make future extensions more difficult
* be easy to type; programs that use it are expected to use it very
* be easy to type; programs that use it are expected to use it very
frequently
* not make it more difficult to scan through code quickly. It should
still be easy to search for all definitions, a particular
* not make it more difficult to scan through code quickly. It should
still be easy to search for all definitions, a particular
definition, or the arguments that a function accepts
* not needlessly complicate secondary support tools such as
@ -105,7 +114,7 @@ The new syntax should
Proposed Syntax
===============
The currently proposed syntax is::
The currently proposed syntax for function decorators is::
def func(arg1, arg2, ...) [dec1, dec2, ...]:
pass
@ -114,6 +123,11 @@ The decorators are near the declaration of the function's API but are
clearly secondary. The square brackets make it possible to fairly
easily break long lists of decorators across multiple lines.
Class decorators are defined in an analogous fashion::
class MyClass(base1, base2) [dec1, dec2, ...]:
pass
Alternate Proposals
===================
@ -123,15 +137,18 @@ A few other syntaxes have been proposed::
pass
The absence of brackets makes it cumbersome to break long lists of
decorators across multiple lines. The keyword "as" doesn't have the
same meaning as its use in the ``import`` statement.
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. :-)
.. _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
::
def [dec1, dec2, ...] func(arg1, arg2, ...):
pass
This form has the disadvantage that the decorators become visually
This form has the disadvantage that the decorators visually assume
higher priority than the function name and argument list.
::
@ -139,11 +156,13 @@ higher priority than the function name and argument list.
def func [dec1, dec2, ...] (arg1, arg2, ...):
pass
Quixote's Page Template Language uses this form, but only supports a
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.
.. _Python Template Language: http://www.mems-exchange.org/software/quixote/doc/PTL.html
::
using:
@ -156,15 +175,16 @@ function name.
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 block structure 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.
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.
Current Implementation
======================
Michael Hudson has posted a `patch`_ at Starship, which implements the
proposed syntax and left-first application of decorators::
Michael Hudson posted a `patch`_ at Starship, which implements the
proposed syntax changes for both functions and classes and left-first
application of decorators::
def func(arg1, arg2, ...) [dec1, dec2]:
pass
@ -177,15 +197,16 @@ is equivalent to::
though without the intermediate creation of a variable named ``func``.
.. _patch: http://starship.python.net/crew/mwh/hacks/meth-syntax-sugar.diff
.. _patch: http://starship.python.net/crew/mwh/hacks/meth-syntax-sugar-3.diff
Examples
========
Much of the discussion on ``comp.lang.python`` and the ``python-dev``
mailing list focuses on the use of the ``staticmethod()`` and
``classmethod()`` builtins. This capability is much more powerful
than that. This section presents some examples of use.
mailing list focuses on the use of decorators as a cleaner way to use
the ``staticmethod()`` and ``classmethod()`` builtins. This
capability is much more powerful than that. This section presents
some examples of use.
1. Define a function to be executed at exit. Note that the function
isn't actually "wrapped" in the usual sense.
@ -224,8 +245,8 @@ than that. This section presents some examples of use.
return f
return decorate
def classmethod(f) [release(versionadded="2.2",
author="Guido van Rossum")]:
def mymethod(f) [release(versionadded="2.2",
author="Guido van Rossum")]:
...
4. Enforce function argument and return types.
@ -234,12 +255,12 @@ than that. This section presents some examples of use.
def accepts(*types):
def check_accepts(f):
assert len(types) == f.func_code.co_argcount
def new_f(*args, **kwds):
for (a, t) in zip(args, types):
assert isinstance(a, t), \
"arg %r does not match %s" % (a,t)
return f(*args, **kwds)
assert len(types) == f.func_code.co_argcount
return new_f
return check_accepts
@ -257,20 +278,43 @@ than that. This section presents some examples of use.
returns((int,float))]:
return arg1 * arg2
5. Declare that a class implements a particular (set of) interface(s).
This is from a posting by Bob Ippolito on ``python-dev`` based on
experience with `PyProtocols`_.
.. _PyProtocols: http://peak.telecommunity.com/PyProtocols.html
::
def provides(*interfaces):
"""
An actual, working, implementation of provides for
the current implementation of PyProtocols. Not
particularly important for the PEP text.
"""
def provides(typ):
declareImplementation(typ, instancesProvide=interfaces)
return typ
return provides
class IBar(Interface):
"""Declare something about IBar here"""
class Foo(object) [provides(IBar)]:
"""Implement something here..."""
Of course, all these examples are possible today, though without the
syntactic support.
Possible Extensions
===================
Open Issues
===========
The proposed syntax is general enough that it could be used on class
definitions as well::
1. It's not yet certain that class decorators will be incorporated
into the language at this point. Guido expressed skepticism about
the concept, but various people have made some `strong arguments`_
(search for ``PEP 318 - posting draft``) on their behalf in
``python-dev``.
class foo(object) [dec1, dec2, ...]:
class definition here
Use would likely be much less than function decorators. The current
patch only implements function decorators.
.. _strong arguments: http://mail.python.org/pipermail/python-dev/2004-March/thread.html
Copyright