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.

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.

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.

+ 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.

- @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

- 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:

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.

+ No new punctuation or keywords (avoids breaking Leo/IPython/existing code)

+ Really nice syntax for assigning attributes, if desired (Not sure I like this overloading, but it /looks/ good)

def func():
.author = "Kevin Butler"
pass

+/0 Syntax obvious visually (Someone will complain that the leading period gets lost - that person should switch to a fixed-width font, as used when coding. <.5 wink>) Easy to highlight.

0 Although it is a punctuation-based syntax, it is compatible with existing/proposed '.' usage ('.' can mean subordination to a containing construct like 'with', and passing the implicit "func" argument is analogous to passing "self")

0 Perhaps decorators should be allowed before or after the docstring. If you have to choose, I'd choose making it before the docstring.

+ Decorators are indented, so it's clear that they modify the function.

+ Reads naturally as "pipe" operator to unix hackers (which is semantically correct, since the defined function gets passed through the decorators, one at a time, and the result is used.

+ Doesn't use up one of the currently unused characters (such as "@") -- it's always possible that we'll find another good use for those later.

0 Perhaps decorators should be allowed before or after the docstring. If you have to choose, I'd choose making it before the docstring.

- "|" is a relatively arbitrary character choice; no intuitive reason why it would indicate a decorator

- Indented decorators look like they should be evaluated when the function is called, not when the function is parsed. I have the same objection to docstrings, though.

- Misleading to Unix hackers, since the order of evaluation is "backwards".

- For some fonts, "|" looks similar to "I" or "l" or "1". Some think that code highlighting would remove this problem, but with normal-sized fonts, there are not many pixels in it to show a color clearly. "@" has a big blob of pixels, and is very distinct. If proper style uses a space after the "|", the decoration will be a syntax error if I, 1, l (or !) are used.

- The key with "|" on it is often in an awkward location on laptop keyboards

- No implementation currently exists.

- When using just one bar, it isn't noticable enough. It seems more a part of the actual decorator function name than something demarking the function.

- When using multiple decorators, the pattern formed by the vertical bars draws the eyes too much and makes it hard to focus on the signature.

An alternative (inspired by a typing error I corrected in E2 in the Guido example) would be to put vbar decorator before the colon... Basically it has the same characteristic than E2, with the following slight differences:

+ More obviously attached to function definition

+ Possible 1-line version as readable as the inline syntax (see F.) for short function/decoration

- More difficult to parse? The end-of-statement rules in Python mandate the colon be on the same line as the close of the argument list (less any line continuations); this syntax breaks that rule. Either the rule would have to be changed (yikes!), or line continuations would have to be used.

- New syntax having no equivalent in other part of python (but all @ / | propositions suffer from that )

+ It doesn't break Leo, IPython, or any other tool that uses @ as a special character.

+ The association with pipes makes some sense: "take this thing and pass it through that thing to get a modified thing".

+ Visual association with function also follows from the vertial continuous line that forms, sort of like a flagpole attached to the function and each decorator like a flag.

0 Less 'in-your-face' than @ (some claim it blends in too much, and others, that it's a good thing)

- Most characters (including pipe) already have a meaning. Ending an expression at the linebreak will probably keep these from becoming ambiguous, but ... that gets fragile.

- Misleading to Unix hackers, since the order of evaluation is "backwards".

- For some fonts, "|" looks similar to "I" or "l" or "1" Some think that code highlighting would remove this problem, but with normal-sized fonts, there are not many pixels in it to show a color clearly. "@" has a big blob of pixels, and is very distinct. If proper style uses a space after the "|", the decoration will be a syntax error if I, 1, l (or !) are used.

- The key with "|" on it is often in an awkward location on laptop keyboards

- When using just one bar, it isn't noticable enough. It seems more a part of the actual decorator function name than something demarking the function.

- When using multiple decorators, the pattern formed by the vertical bars draws the eyes too much and makes it hard to focus on the signature.

- way to much indenting if there are multiple decorators. (see following question)

- Determining which decorators apply to a function requires scanning an arbitrarily large amount of code (with real functions and nesting, the decorator can be literally hundreds of lines away from the def).

- In practice, functions are likely to each require slightly different decorator sets (other may argue that most users would only use staticmethod and don't care grouping them), which means that we either do not group them (in which case, the new indent level doesn't gain us anything), or functions will be heavily and unevenly nested. (See below for an example.)

Here's an example of run-away nesting (imagine trying to figure out which decorators apply to baz if these were non-trivial functions) (answer: but you can do the same with if, and it doesn't mean you have to... The main point of this proposal is to allow one level of indentaton, in which case it is clear, but having the drawback of the indentation. If you find three level of indentation unclear, why write code that way? No other proposition has this "grouping" capacity anyway...) (answer answer: The original point was only that they can all be abused, but that this syntax can be abused in a way that is unique, simply because of the grouping capability.):

(I have changed the keyword in the second example to "using", to match the recommendation in Robert's paper. I have left the first example alone, to indicate that the precise choice of keyword is independent of the use of a suite.)

Note that this differs from J5 (and largely from J4) only in whether the function signature stays with the function body, or moves to the top suite. (And in whether the redundant "@" is added for extra readability.)

+ Uses widely known python syntax, albeit in an unusual way

+ Reads well and naturally

+ Doesn't need extra characters with special meaning

+ Does not use up one of the few remaining "no meaning anywhere" characters.

+ Docstring can be moved to Guido's favourite place, i.e. before the def suite

+ Moves decorators, signature and implementation closer together for functions with a long docstring

+ With the right keyword, e. g. "transform", a new user gets a clear notion of the suite's purpose

+ Can smoothly be extended to a full-blown suite allowing for if __debug__: trace etc.

(- No one can agree on the right keyword to use.) The consensus is "using".

- Keyword chosen may conflict with how keyword is used in other languages (like "using" in C# or "extends" in Java and other languages).

- Keyword may be confused with other keywords in current and future versions of Python (like "with" in Python3.0, which is very similar to "using"). This would suggest choosing a less generic keyword like "decorate", "meta", or "predef".

(- Indented decorators put them visually in line with the function name. When indented below the function declaration line this isn't a big deal, but right above the function declaration it can be a problem.) This is so minor that it ought to be ignored.

(- Much more typing required than A1 pie syntax the more decorators you have.) But less shifting. (You also have to do more indentation checking.) Any decent editor does this with one keystroke. Try typing the two:

- Overkill for the simple case like classmethod (not that much, since "@" can be replaced by "using:" without new line). (Two extra lines still feels like overkill to me; a line with an internal colon is less overkill, but has its own style problems.)

- Many people felt it was wrong use of an identation suite. (Someone please explain. It looks just like any other pair of blocks in Python, such as if/else or try/finally.) In current python, an indent either adds a new namespace (this doesn't, it modifies the parent namespace where the def itself appears) or changes flow control. I do think it changes flow control as much as an if or a loop, but others felt strongly that it wasn't the same, and that the equivalence of decorators within a decorate block was different from the equivalence of statements in a function block. For a sample of the response, search the python-dev archives around March 2004. Private email was more adamant.

- Has the same problem as option A: in that the decorate block implicitly affects the following def. This does not occur elsewhere in Python. -- Or more precisely, it doesn't occur with an indented block syntax; parentheses can affect execution order. The proposed with: block would do the same thing. No, the with block wouldn't affect blocks that come after the with block like this does.

- Same problem as A1 and other proposals, in what order are the decorators applied? C1 is just about the only proposal that doesn't suffer from this problem.

- Where will the docstring go? Guido wants the docstring before the function. (He said it would be nice if it went there, that's all.) Does it have to be at the beginning of the decorate: indentation block? Or would it have to be at the end? Or where? What if the function has no decorators, would you have to still use "decorate" followed by an indented docstring? (Not before Python 3000, if ever.)

- Technical problems with the current grammar parser if a suite *starts* with an optional part. (Ending with an optional part, such as "else:" is OK, but starting with one is not.) (Michael Sparks has experimented with the grammar and based on his results it appears *not* an issue.)

The implementation referenced above does NOT allow a generic suite - but only allows decorators to be listed. The semantics wrt to ordering remains the same as in 2.4a2

(Instead of the word "decorate" it is possible to use some other less lengthy keyword. And even DEF taken uppercase.)

Note that this differs from J5 only by the colon. J5 in turn differs from J2 in whether the function signature stays with the function body, or moves to the top suite. (And in whether the redundant "@" is added for extra readability.)

+ doesn't require new keyword ("as" can be unambiguously parsed in context)

+ Leverages the visibility of "@" to overcome visibility problems

of list-after-def syntaxes

+ Even better definition-reflects-usage than the current method

+ Adds a well-defined header, but still backwards-compatible, because of the @ leading each line in the header. (So a non-@ line means an old-style definition, rather than a header.)

+ Gives the option of putting the docstring as the final decorator, so that wrapping decorators don't have to take care to restore it.

- Nothing to do with signification of "as" in other languages and Python. (Other words might work, but it isn't clear that any are better. Maybe "from"?)

- There is no line-continuating character, and the indentation cannot be enforced. (Not true! It *can* be enforced; the proposed grammar calls for an INDENT/DEDENT pair wrapping the decorators. The actual downside is that it will be the first such required indent that is not preceded by a colon.)

+ The lack of a colon emphasizes that the following items (decorators) are semantically closer to an inline list than a full-blown suite.

if (test): # They match the suite, rather than the statements
stmt1
stmt2

- Some may argue that the "as" is not necessary and that the syntax is very similar to a in-body syntax. (The unindented "as" is precisely what separates this from the in-body syntax. To me, the difference is startling.)

J5 two part def suite with "@" decorators and colon

While the missing colon of J4 was intentional, there are good arguments both ways. Since we're listing options, I include this variant of J4.

Note that J5 differs from J2 only in whether the function signature stays with the function body, or moves to the top suite. (And in whether the redundant "@" is added for extra readability.)

+ doesn't require new keyword ("as" can be unambiguously parsed in context)

+ Leverages the visibility of "@" to overcome visibility problems

of list-after-def syntaxes

+ definition-reflects-usage equal to current function definitions.

+ Adds a well-defined header, but still backwards-compatible, because of the @ leading each line in the header. (So a non-@ line means an old-style definition, rather than a header.)

+ Gives the option of putting the docstring as the final decorator, so that wrapping decorators don't have to take care to restore it.

- Nothing to do with signification of "as" in other languages and Python. (Other words might work, but it isn't clear that any are better. Maybe def: ... from: ?)

+ Colon/linebreak consistency with the rest of the language.

- Some may argue that the "as" is not necessary and that the syntax is very similar to a in-body syntax. (The unindented "as" is precisely what separates this from the in-body syntax. To me, the difference is startling.)

K. partitioned syntax syntax

Use pie-decorator syntax (or some other complex syntax) when arguments

are to be passed

use inline syntax when no arguments are necessary

def classmethod foo(arg1,arg2):
...

+ Simple for simple cases, poweful when needed

+ Obviousely attached to the function

- The natural place where everyone looks for the function name now is a possible container for other information

This is debatable. "Natural" will change if this is accepted. The natural place to find the fnction name will be after any simple decorators and before the argument list.

- Complicates things like colorization and other functions of helper tools

Other syntaxes will need to be colorized too and will thus complicate colorization.

- Since both methods are legal, it has all the downsides of either syntax, in terms of what it does to the rest of the language or newbie confusion.

M. Making def an expression / letting it return a value

+ Because of the (), the function definition can be (but does not need to be) indented an extra level, depending on what makes sense.

+ Familiar from other functions-as-first-class-objects languages, such as LISP.

- The delayed close-paren is better than the current format, but still not desirable.

- Guido does not want to blur the distinction between statements and expressions.

0 The name would be temporarily bound to the unwrapped function, which changes semantics for some types of wrappers, such as generics and properties.

N: Another Proposal (supplied by Anonymous)

# using Guidos example
# there may even be some leading character to identify them even more easily
# (preferred by me would be a single dot in front of the first one, chaining
# subsequent ops-to-be-applied by a | pipe suggesting applying in sequence.)
class C(object):
# decorators have to go before the (function) definition, indented
# this actually enforces separation from any preceding code by blank lines
# (which is good imho)
staticmethod
funcattrs(grammar="'\n' (' '*) dotted_name [ '(' [arglist] ')' ] "
+ "['\n' \1 dotted_name [ '(' [arglist] ')' ] ]*"
+ "['\n' [docstring] ]",
status="experimental",
author="htr@aon")
"""This method blah, blah.
It supports the following arguments:
- longArgumentOne -- a string giving …
- longArgumentTwo -- a number giving …
blah, blah.
"""
def longMethodNameForEffect(longArgumentOne=None,
longArgumentTwo=42):
raise NotYetImplemented

* - needs slightly more elaborate tracking of indentation level

* - there might arise a need to comment blank lines in function

(how about requiring two blank lines ahead of decorators?)

then again, that's not far from introducing another keyword

or just use the ".first_dec \n |snd_dec \n |trd_dec" syntax

* 0 makes whitespace even more significant (but eh, that's python)

* + easy to parse, integrating well into the language

* + everything easily located at a sensible place

* - [please add drawbacks here]

Decorator Syntax Breakdown

Here's a breakdown of some of the different decisions that have to be made in deciding on a decorator syntax. This is an attempt to consolidate some of the common points from the various examples above.

Indicator

Proposals differ on how some sort of indicator of "decoratorhood" is use. These include:

Keyword

+ Makes it clear that this is not a normal statement

+ Will not be silently ignored

+ Makes syntax highlighting easy

0 Will make the semantics clear -- but may therefore be misleading for some types of decorators. (Can you think of a word that works well with all of classmethod, annotations, adding a transaction capability, registering the function with a generic method, and replacing the function with something else depending on runtime environment? If so, you're partway there.)

- New keywords can break existing code

Some keyword options:

A. decorate

+ Not confusable with any current Python keyword

+ Intuitively associated with decorator semantics

B. transform

+ Not confusable with any current Python keyword

+ Clear indication that a function may change its signature and effect, not only its attributes

+ Evokes "quality" or "qualifier" concepts, but is not limited to either

0 Does not have any meaning itself

Symbol

+ Makes it clear that this is not a normal statement

+ Will not be silently ignored

+ Saves keystrokes

- Introduces a new character in the language. This either overloads an old character with new meaning, or uses up an increasingly scarce resource. (There are only three unused characters now, and none are reserved to the user.)

Some symbol options:

@

+ Java-like, so not completely unknown.

+ Not confusable with any current Python symbol

+ Makes syntax highlighting easy

0 Used in other languages to mean "attribute"

- Already used as a special character in IPython and Leo

- Not intuitively associated with the decorator semantics

|

+ Vertical bars visually "attach" the decorators to the name (with a vertical ASCII art line).

0 Reminiscent of Unix "pipe" operations (though order may be "backwards")

- Not intuitively associated with the decorator semantics

- Gives substantially new meaning to '|', may be confusing for beginners. Adds new obfuscation possibilities depending on line continuations.

- For some fonts, "|" looks similar to "I" or "l" or "1".

- The key with "|" on it is often in an awkward location on laptop keyboards

- Narrow character, may not be obvious enough

%

+ Already associated in Python with some sort of "application" type semantics.

- Not intuitively associated with the decorator semantics

Function

+ Does not require a syntax change

- Changes semantics of function calls. (A function call followed by a function definition is not usually assumed to be affecting that definition.)

None

+ With list syntax, C# like, so not completely unknown.

- In pre-def and post-def locations, looks like a normal statement, but does not behave like one.

- In pre-def and post-def locations, may be silently ignored

- In pre-def and post-def locations, may be hard to highlight in an editor.

- In pre-def locations, would not work in interactive mode (list would be interpreted right away)

- In within-def locations, does not make clear the break between decorators and the rest of the function declaration

Location

Proposals also differ on exactly where the decorator declaration should be made. These include:

Pre-def

+ Decorators are easy to find

+ Won't break simplistic code analyzers or grep for function def

0 Not consistent with docstring location

- Decorators are not as clearly a part of the function declaration

- Following empty lines or comments may separate decorators from function declaration.

+ Should not require extra mental "stack space" to process decorators (function name has already been seen)

- May break simplistic code analyzers or grep for function def

- Easy to miss the transition between a long argument list and a long decorator list

- Breaks the symmetry between definition and use -- you can no longer just cut and paste the definition to write a call, or vice versa.

- Guido has ruled it out, because of the separation between function name and function arguments.

Between argument list and colon

+ Should not require extra mental "stack space" to process decorators (function name has already been seen)

+ Won't break simplistic code analyzers or grep for function def

- Easy to miss the transition between a long argument list and a long decorator list

Post-def

+ Should not require extra mental "stack space" to process decorators (function name has already been seen)

+ Won't break simplistic code analyzers or grep for function def

0 Consistent with docstring location

- Decorators may look like part of the function's code, not its declaration. (That is, they look like they're evaluated when the function is called, not when it is parsed.) This is a problem for decorations that change the signature, like staticmethod.

0 Different semantics from that of other paired, nested blocks (e.g. if/elif/else, try/except, try/finally)

- Technical problems if a block *starts* with an optional part? (Someone said yes, someone else said that they tried it and had no problems.)

- David Eppstein points out that most indented suites can contain arbitrary code. It isn't clear what should happen if someone uses a statement (does not return a value); should it be run once? Should it cause an error?

No indent

- Uncommon in Python that one line following another one with the same identation has a meaning like this.

Order of Decorators

Should decorators be applied in textual order, or in reverse order (as if they were in nested parentheses around the def).

It seems to have been settled that decorators will be applied starting with the one closest to the "def" statement. (This means in-order vs reverse will depend on the eventual location.)

Allowable Decorators

The eventual decision was that anything should be allowed, but it *will* be called with a single argument (the function object) before the function's name is bound in the enclosing namespace.

Should None be allowed and just not called? This would allow statements in the decorator suite, and might be useful, but ... it wasn't deemed useful enough for a special case, at least in 2.4

The temporary variarables aren't really created. Those are just placeholders to indicate that the expression is evaluated (hopefully to produce a callable) before the function definition.

The function can be any function, not just a lambda expression (which is more restricted).

Thinking ahead to Python 3 ?

Christopher King makes the point that we are trying to do too much with decorators: declare class/static methods, describe function metadata, and mangle functions. It might be best to think about what is best for each separately.