2003-06-09 00:04:58 -04:00
PEP: 318
2004-03-25 16:45:58 -05:00
Title: Decorators for Functions, Methods and Classes
2003-06-09 00:04:58 -04:00
Version: $Revision$
Last-Modified: $Date$
2004-03-23 11:41:17 -05:00
Author: Kevin D. Smith <Kevin.Smith@theMorgue.org>,
Jim Jewett <jimjjewett@users.sourceforge.net>,
Skip Montanaro <skip@pobox.com>
2003-06-09 00:04:58 -04:00
Status: Draft
Type: Standards Track
2004-03-23 11:41:17 -05:00
Content-Type: text/x-rst
2003-06-09 00:04:58 -04:00
Created: 05-Jun-2003
Python-Version: 2.4
2004-03-23 11:41:17 -05:00
Post-History: 09-Jun-2003, 10-Jun-2003, 27-Feb-2004, 23-Mar-2004
2003-06-09 00:04:58 -04:00
Abstract
2004-03-23 11:41:17 -05:00
========
2004-03-25 16:45:58 -05:00
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
2004-03-23 11:41:17 -05:00
transformations of a declaration.
2003-06-09 00:04:58 -04:00
Motivation
2004-03-23 11:41:17 -05:00
==========
2003-06-09 00:04:58 -04:00
2004-03-23 11:41:17 -05:00
The current method of applying a transformation to a function or
method places the actual translation after the function body. For
large functions this separates a key component of the function's
behavior from the definition of the rest of the function's external
interface. For example::
2003-06-09 00:04:58 -04:00
2004-03-23 11:41:17 -05:00
def foo(self):
perform method operation
foo = classmethod(foo)
2003-06-09 00:04:58 -04:00
2004-03-23 11:41:17 -05:00
This becomes less readable with longer methods. It also seems less
than pythonic to name the function three times for what is
conceptually a single declaration. A solution to this problem is to
move the transformation of the method closer to the method's own
declaration. While the new syntax is not yet final, the intent is to
replace::
2003-06-10 00:29:48 -04:00
2004-03-23 11:41:17 -05:00
def foo(cls):
pass
foo = synchronized(lock)(foo)
foo = classmethod(foo)
2003-06-09 00:04:58 -04:00
2004-03-23 11:41:17 -05:00
with an alternative that places the decoration in the function's
declaration::
2003-06-09 00:04:58 -04:00
2004-03-23 11:41:17 -05:00
def foo(cls) using [synchronized(lock), classmethod]:
pass
2003-06-09 00:04:58 -04:00
2004-03-25 16:45:58 -05:00
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.
2004-03-23 11:41:17 -05:00
Background
==========
2003-06-09 00:04:58 -04:00
2004-03-23 11:41:17 -05:00
There is general agreement that syntactic support is desirable to the
current state of affairs. Guido mentioned `syntactic support for
decorators`_ in his DevDay keynote presentation at the `10th Python
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
2004-03-25 16:45:58 -05:00
``comp.lang.python`` by `Gareth McCaughan`_.
2003-06-09 00:04:58 -04:00
2004-03-23 11:41:17 -05:00
.. _syntactic support for decorators: http://www.python.org/doc/essays/ppt/python10/py10keynote.pdf
.. _10th python conference: http://www.python.org/workshops/2002-02/
.. _michael hudson raised the topic: http://mail.python.org/pipermail/python-dev/2002-February/020005.html
.. _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
2003-06-09 00:04:58 -04:00
2004-03-25 16:45:58 -05:00
Class decorations seem like an obvious next step because class
definition and function definition are syntactically similar.
2004-03-23 11:41:17 -05:00
Design Goals
============
2003-06-09 00:04:58 -04:00
2004-03-23 11:41:17 -05:00
The new syntax should
2003-06-09 00:04:58 -04:00
2004-03-23 11:41:17 -05:00
* work for arbitrary wrappers, including user-defined callables and
the existing builtins ``classmethod()`` and ``staticmethod``
2003-06-09 00:04:58 -04:00
2004-03-23 11:41:17 -05:00
* work with multiple wrappers per definition
2003-06-09 00:04:58 -04:00
2004-03-25 16:45:58 -05:00
* 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
2004-03-23 11:41:17 -05:00
code
2003-06-09 00:04:58 -04:00
2004-03-23 11:41:17 -05:00
* not make future extensions more difficult
2003-06-10 00:29:48 -04:00
2004-03-25 16:45:58 -05:00
* be easy to type; programs that use it are expected to use it very
2004-03-23 11:41:17 -05:00
frequently
2003-06-10 00:29:48 -04:00
2004-03-25 16:45:58 -05:00
* not make it more difficult to scan through code quickly. It should
still be easy to search for all definitions, a particular
2004-03-23 11:41:17 -05:00
definition, or the arguments that a function accepts
2003-06-09 00:04:58 -04:00
2004-03-23 11:41:17 -05:00
* not needlessly complicate secondary support tools such as
language-sensitive editors and other "`toy parser tools out
there`_"
2003-06-10 00:29:48 -04:00
2004-03-23 11:41:17 -05:00
.. _toy parser tools out there: http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=mailman.1010809396.32158.python-list%40python.org
2003-06-10 00:29:48 -04:00
2004-03-23 11:41:17 -05:00
Proposed Syntax
===============
2003-06-10 00:29:48 -04:00
2004-03-25 16:45:58 -05:00
The currently proposed syntax for function decorators is::
2004-02-28 14:09:44 -05:00
2004-03-23 11:41:17 -05:00
def func(arg1, arg2, ...) [dec1, dec2, ...]:
pass
2004-02-28 14:09:44 -05:00
2004-03-23 11:41:17 -05:00
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.
2004-02-28 14:09:44 -05:00
2004-03-25 16:45:58 -05:00
Class decorators are defined in an analogous fashion::
class MyClass(base1, base2) [dec1, dec2, ...]:
pass
2004-03-23 11:41:17 -05:00
Alternate Proposals
===================
2004-02-28 14:09:44 -05:00
2004-03-23 11:41:17 -05:00
A few other syntaxes have been proposed::
2004-02-28 14:09:44 -05:00
2004-03-23 11:41:17 -05:00
def func(arg1, arg2, ...) as dec1, dec2, ...:
pass
2004-02-28 14:09:44 -05:00
2004-03-23 11:41:17 -05:00
The absence of brackets makes it cumbersome to break long lists of
2004-03-25 16:45:58 -05:00
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
2004-02-28 14:09:44 -05:00
2004-03-23 11:41:17 -05:00
::
2004-02-28 14:09:44 -05:00
2004-03-23 11:41:17 -05:00
def [dec1, dec2, ...] func(arg1, arg2, ...):
pass
2004-02-28 14:09:44 -05:00
2004-03-25 16:45:58 -05:00
This form has the disadvantage that the decorators visually assume
2004-03-23 11:41:17 -05:00
higher priority than the function name and argument list.
2004-02-28 14:09:44 -05:00
2004-03-23 11:41:17 -05:00
::
2004-02-28 14:09:44 -05:00
2004-03-23 11:41:17 -05:00
def func [dec1, dec2, ...] (arg1, arg2, ...):
pass
2004-02-28 14:09:44 -05:00
2004-03-25 16:45:58 -05:00
Quixote's `Python Template Language`_ uses this form, but only supports a
2004-03-23 11:41:17 -05:00
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.
2003-06-09 00:04:58 -04:00
2004-03-25 16:45:58 -05:00
.. _Python Template Language: http://www.mems-exchange.org/software/quixote/doc/PTL.html
2004-03-23 11:41:17 -05:00
::
2003-06-09 00:04:58 -04:00
2004-03-23 11:41:17 -05:00
using:
dec1
dec2
...
def foo(arg1, arg2, ...):
pass
2003-06-10 00:29:48 -04:00
2004-03-23 11:41:17 -05:00
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
2004-03-25 16:45:58 -05:00
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.
2003-06-09 00:04:58 -04:00
2004-03-23 11:41:17 -05:00
Current Implementation
======================
2003-06-10 00:29:48 -04:00
2004-03-25 16:45:58 -05:00
Michael Hudson posted a `patch`_ at Starship, which implements the
proposed syntax changes for both functions and classes and left-first
application of decorators::
2003-06-10 00:29:48 -04:00
2004-03-23 11:41:17 -05:00
def func(arg1, arg2, ...) [dec1, dec2]:
pass
2003-06-10 00:29:48 -04:00
2004-03-23 11:41:17 -05:00
is equivalent to::
2003-07-29 11:31:13 -04:00
2004-03-23 11:41:17 -05:00
def func(arg1, arg2, ...):
pass
func = dec2(dec1(func))
2004-02-28 14:09:44 -05:00
2004-03-23 11:41:17 -05:00
though without the intermediate creation of a variable named ``func``.
2003-07-29 11:31:13 -04:00
2004-03-25 16:45:58 -05:00
.. _patch: http://starship.python.net/crew/mwh/hacks/meth-syntax-sugar-3.diff
2003-07-29 11:31:13 -04:00
2004-03-23 11:41:17 -05:00
Examples
========
2003-06-10 00:29:48 -04:00
2004-03-23 11:41:17 -05:00
Much of the discussion on ``comp.lang.python`` and the ``python-dev``
2004-03-25 16:45:58 -05:00
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.
2003-06-10 00:29:48 -04:00
2004-03-23 11:41:17 -05:00
1. Define a function to be executed at exit. Note that the function
isn't actually "wrapped" in the usual sense.
::
def onexit(f):
import atexit
atexit.register(f)
return f
def func() [onexit]:
...
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``.)
::
def singleton(cls):
return cls()
2003-06-10 00:29:48 -04:00
2004-03-23 11:41:17 -05:00
class MyClass [singleton]:
...
2003-06-10 00:29:48 -04:00
2004-03-23 11:41:17 -05:00
3. Decorate a function with release information. (Based on an example
posted by Anders Munch on ``python-dev``.)
2003-06-10 00:29:48 -04:00
2004-03-23 11:41:17 -05:00
::
2003-06-09 00:04:58 -04:00
2004-03-23 11:41:17 -05:00
def release(**kwds):
def decorate(f):
for k in kwds:
setattr(f, k, kwds[k])
return f
return decorate
2003-06-09 00:04:58 -04:00
2004-03-25 16:45:58 -05:00
def mymethod(f) [release(versionadded="2.2",
author="Guido van Rossum")]:
2004-03-23 11:41:17 -05:00
...
4. Enforce function argument and return types.
::
def accepts(*types):
def check_accepts(f):
2004-03-25 16:45:58 -05:00
assert len(types) == f.func_code.co_argcount
2004-03-23 11:41:17 -05:00
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)
return new_f
return check_accepts
def returns(rtype):
def check_returns(f):
def new_f(*args, **kwds):
result = f(*args, **kwds)
assert isinstance(result, rtype), \
"return value %r does not match %s" % (result,rtype)
return result
return new_f
return check_returns
def func(arg1, arg2) [accepts(int, (int,float)),
returns((int,float))]:
return arg1 * arg2
2004-03-25 16:45:58 -05:00
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
2004-03-29 22:48:59 -05:00
2004-03-25 16:45:58 -05:00
::
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..."""
2004-03-23 11:41:17 -05:00
Of course, all these examples are possible today, though without the
syntactic support.
2004-03-25 16:45:58 -05:00
Open Issues
===========
2003-06-09 00:04:58 -04:00
2004-03-25 16:45:58 -05:00
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``.
2003-06-09 00:04:58 -04:00
2004-03-25 16:45:58 -05:00
.. _strong arguments: http://mail.python.org/pipermail/python-dev/2004-March/thread.html
2003-06-09 00:04:58 -04:00
Copyright
2004-03-23 11:41:17 -05:00
=========
2003-06-09 00:04:58 -04:00
2004-03-23 11:41:17 -05:00
This document has been placed in the public domain.
2003-06-09 00:04:58 -04:00
Local Variables:
mode: indented-text
indent-tabs-mode: nil
sentence-end-double-space: t
fill-column: 70
End: