Contents
- About this page
- What is a Decorator
- What is a Python Decorator
- Debate about decorators in Python
- Examples
-
Current Python Decorator Proposals
- A1. pie decorator syntax
- A2. pie decorator and space syntax
- B. list-before-def syntax
- C1. list-after-def syntax
- C2. list-after-def syntax with a (pseudo-)keyword
- C3. tuple-after-def syntax with a (pseudo-)keyword
- C4. tuple-after-def syntax with a % operator
- D1. list at top of function body syntax
- D2. 'dot'decorators at top of function body syntax
- E1. pie decorator at top of function body syntax
- E2. vbar decorator at top of function body syntax
- E3. vbar decorator after arg
- E4. keyword decorator at top of function body syntax
- F. inline syntax
- F2. inline syntax + new keyword (decodef for example)
- G. as decorator
- H. pie decorator using a different character
- I. angle brackets decorator syntax
- J1. new keyword decorator syntax
- J2. expand the def suite
- J3. two part def suite
- J4 two part def suite with "@" decorators
- J5 two part def suite with "@" decorators and colon
- K. partitioned syntax syntax
- L. Keyword other than as and with before def
- M. Making def an expression / letting it return a value
- N: Another Proposal (supplied by Anonymous)
- Decorator Syntax Breakdown
- Thinking ahead to Python 3 ?
About this page
This page largely documents the history of the process of adding decorators to Python.
If you're just interested in what decorators or the '@' symbol mean in Python, see the Wikipedia page http://en.wikipedia.org/wiki/Python_syntax_and_semantics#Decorators or PEP 318.
What is a Decorator
A decorator is the name used for a software design pattern. Decorators dynamically alter the functionality of a function, method, or class without having to directly use subclasses or change the source code of the function being decorated.
For more information about the decorator pattern in general, see:
What is a Python Decorator
The "decorators" we talk about with concern to Python are not exactly the same thing as the DecoratorPattern described above. A Python decorator is a specific change to the Python syntax that allows us to more conveniently alter functions and methods (and possibly classes in a future version). This supports more readable applications of the DecoratorPattern but also other uses as well.
Support for the decorator syntax was proposed for Python in PEP 318, and will be implemented in Python 2.4.
Note that the current proposal actually only decorates functions (including methods). Extending it to classes or even arbitrary code is possible, but Guido wasn't sure it made sense. (Later versions might become more permissive, but they can't easily snatch functionality back.)
Debate about decorators in Python
The winning syntax as of now uses the '@' symbol, as described in this message. Mark Russell implemented this version. Here is the message describing the patch he checked in.
There has been a long discussion about the syntax to use for decorators in Python.
Examples
See PythonDecoratorLibrary for more complex and real-world examples. See also MixIns and MetaClasses for related resources.
Current Python Decorator Proposals
See section 6 for a categorization of different proposals; this section just provides concrete examples.
Decorator Poll (consider the poll now closed)
Here is an online poll where you can vote between a few different alternative syntaxes for python decorators: http://wiki.wxpython.org/index.cgi/PythonDecoratorsPoll
A more complete poll is currently running on comp.lang.python, with the unfortunate name of "Alternate decorator syntax decision", using the options on this WikiPage as the candidates. Please visit that thread and express your preference!
After the @decorator syntax was "accepted", lots of people threw up alarms and a huge series of threads started exploding on Python-dev. Here are the current alternatives that I could find that are being argued, with pros and cons.
I give two examples that might be common uses in the future. Classmethod declarations, and something like static typing (adapters), declaring what type parameters a function expects and returns.
A1. pie decorator syntax
@classmethod
def foo(arg1,arg2):
...
@accepts(int,int)
@returns(float)
def bar(low,high):
...- + Implementation already exists (and is in Python 2.4a2)
- + Java-like, so not completely unknown to everyone.
- + Makes the syntax obvious visually (i.e., obviously not a normal statement)
- + Will not be silently ignored
- + Compile-time
- + One decorator per line (makes it clearer to read, write and change order of decorators)
- + Separate from the def syntax (desired by some for making decoration stand out and keeping def the same)
- + Currently not used in Python so @decorators can be inserted anywhere, such as after parameters or inside expressions.
- + All decorators line up in one column immediately above the function name (easy to browse and see what's going on).
- + The @ special character will make syntax highlighting easier than it would be for normal statements in "magic" locations.
- - Separate from the def syntax (undesired by some for simple decorators like classmethod/staticmethod)
- - Ugly?
- - Introduces a new character in the language
- - The @ special character is used in IPython and Leo
- - Punctuation-based syntax raises Perlfears. But no "obvious" keyword or expression has been suggested.
- - @ is a relatively arbitrary character choice; no intuitive reason why it would indicate a decorator
- 0 The @ character is often used (in other languages) to mean "attribute". For annotations, this is good. For more active decorators, it may not be so good.
- - Some people would like the @ character to be available as an overloadable operator - for example, as a binary operator for matrix multiplication (element-by-element and matrix multiplication may be usefully applied to a single object, so there's arguably a need for a new operator rather than simply overriding binary * on a Matrix class).
- - Because of no indentation, looks confusing when definitions are not separated by empty lines. But adding empty lines makes it hard to determine where the function definition truly starts.
- - That's the first case in Python where a line following another one with the same identation has a meaning.
- - Breaks in interactive shell
- - Comes before the def keyword. Supercedes the function declaration itself, thus applying modifications implicitly to something not yet defined.
- - @staticmethod may look like staticmethod is not a defined variable, but a compile time option
- - Requires the programmer to scan an arbitrary distance from the def in *both* directions to see everything of interest (arg, decs, etc.).
- - Cannot be "folded" by editors as easily, as an arbitrary number of prefix lines should be folded with the body (otherwise, folding becomes less useful).
- - While the Language Spec does not promise that this character won't be used, neither does it reserve *any* character for user extensions. '@' is one of only three that are available, and another '$' is also likely to get used up by 2.4
FWIW, here is Guido's jumble example in this syntax.
class C(object):
@staticmethod
@funcattrs(grammar="'@' dotted_name [ '(' [arglist] ')' ]",
status="experimental", author="BDFL")
def longMethodNameForEffect(longArgumentOne=None,
longArgumentTwo=42):
"""This method blah, blah.
It supports the following arguments:
- longArgumentOne -- a string giving ...
- longArgumentTwo -- a number giving ...
blah, blah.
"""
raise NotYetImplementedAnd here is an example taken from the current test_decorators.py. This exposes the problem of using two lines together with some meaning but without identation or vertical whitespace.
class TestDecorators(unittest.TestCase):
...
def test_dotted(self):
decorators = MiscDecorators()
@decorators.author('Cleese')
def foo(): return 42
self.assertEqual(foo(), 42)
self.assertEqual(foo.author, 'Cleese')
A2. pie decorator and space syntax
@ classmethod
def foo(arg1,arg2):
...
@ accepts(int,int)
@ returns(float)
def bar(low,high):
...* 0 This is more readable for some people, less readable for others.
B. list-before-def syntax
[classmethod]
def foo(arg1,arg2):
...
[accepts(int,int), returns(float)]
def bar(low,high):
...- + Implementation already exists
- + C# like
- + Can be made backwards compatible-ish, with a "hack"
- + Doesn't cause breakage in existing code-searching tools
- + Use visually existing syntax
- - Doesn't cause breakage in existing code-searching tools
- - Would not work in interactive mode (list would be interpreted right away)
- EuroPython didn't like it ( why? ) (hard to teach newbies about the magic)
- - The backwards compatability wouldn't be portable to Jython
- - Looks like a normal expression, but has "magic" behavior of altering a function object
- - Breaks in interactive shell
- - Harder to highlight (looks like a normal list)
C1. list-after-def syntax
def foo(arg1,arg2) [classmethod]:
...
def bar(low,high) [accepts(int,int), returns(float)]:
...- + Implementation already exists
- + Also somewhat C#-like
- + Was a "community favorite" at one time
- + Clearly a part of function declaration
- + Looks ok for simple decorators such as classmethod
- + Won't break simplistic code analyzers or grep for function def
- + Use visually existing syntax
- + Allows one-liner definitions
- - Long lists of decorators/arguments cause ugly line wraps
- - Little to distinguish it visually from argument list
- or 0 For long argument list, decorators are very far from def. I see being too close to def as a minus.
- - Guido hates it because it hides crucial information after the signature, it's easy to miss the transition between a long argument list and a long decorator list, and it's cumbersom to cut and paste a decorator list for reuse.
- - Creates another meaning for []. Since it does so inside the function definition, it will be a distraction for beginners.
- 0 Brackets are used in other fields to indicate an annotation of some sort (but in Python, it doesn't)
I don't see why longs lists of decorators are an issue with this syntax. Consider the following example:
def foo(arg1, arg2) [
complicated(manyArgs=1, notTooUgly='yes'),
even_more_complicated(42)]:
...That doesn't look particularly ugly to me.
---
It also isn't very long.
Here is an example Guido just sent to python-dev:
class C(object):
def longMethodNameForEffect(longArgumentOne=None,
longArgumentTwo=42) [
staticmethod,
funcattrs(grammar="'@' dotted_name [ '(' [arglist] ')' ]",
status="experimental", author="BDFL")
]:
"""This method blah, blah.
It supports the following arguments:
- longArgumentOne -- a string giving ...
- longArgumentTwo -- a number giving ...
blah, blah.
"""
raise NotYetImplementedAnd he editorializes:
That's a total jumble of stuff ending with a smiley. (True story: I
left out the colon when typing up this example and only noticed in
proofing.)
Problems with this form:
- it hides crucial information (e.g. that it is a static method)
after the signature, where it is easily missed
- it's easy to miss the transition between a long argument list and a
long decorator list
- it's cumbersome to cut and paste a decorator list for reuse, because
it starts and ends in the middle of a line
Given that the whole point of adding decorator syntax is to move the
decorator from the end ("foo = staticmethod(foo)" after a 100-line
body) to the front, where it is more in-your-face, it should IMO be
moved all the way to the front.
C2. list-after-def syntax with a (pseudo-)keyword
def foo(arg1,arg2) using [classmethod]:
...
def bar(low,high) using [accepts(int,int), returns(float)]:
...This combines C1 with a keyword; it general, it has all the advantages of either, so I will only list those that are unique to the combination.
- + Groups the decorators with a list, but explains what they are doing, so the list no longer has a magical new meaning.
- + Easily extended; No special characters are "used up", and in the future, other pseudo-keywords could be added.
- + The pseudo-keyword makes it easier to see the separation between the argument tuple and the decorator list.
- + The pseudo-keyword can act as an implicit line-continuation, which helps with (but does not solve) the midline problem.
- - Some proponents objected to adding a keyword, because of more typing.
- 0 Makes the decoration look like a second-class or optional part of the definition. This is true, but may have caused some emotional objection.
- - There was very little agreement on which word should be used.
- No implementation currently exists (it may be a simple variation of the implementation of C1).
FWIW, here is Guido's jumble example in this syntax.
class C(object):
def longMethodNameForEffect(longArgumentOne=None,
longArgumentTwo=42) using
[staticmethod,
funcattrs(grammar="'@' dotted_name [ '(' [arglist] ')' ]",
status="experimental", author="BDFL")]:
"""This method blah, blah.
It supports the following arguments:
- longArgumentOne -- a string giving ...
- longArgumentTwo -- a number giving ...
blah, blah.
"""
raise NotYetImplementedWithout the pseudo-keyword acting as line continuation it reads :
class C(object):
def longMethodNameForEffect(longArgumentOne=None,
longArgumentTwo=42) using [
staticmethod,
funcattrs(grammar="'@' dotted_name [ '(' [arglist] ')' ]",
status="experimental", author="BDFL")]:
"""This method blah, blah.
