Last Updated: Thursday 20th March 2014

Decorators in Python seem complicated, but they're very simple. You've probably seen them; they're the odd bits before a function definition that begin with '@', e.g.:

Note the function called decorator; it takes a function as an argument and defines and returns a new function that uses the one it was passed. That pattern is common to almost all decorators. The @decorator notation is simply a special syntax to call an existing function, passing the new function as an argument, and use the return value to replace the new function.

In the example above, then, decorator is called with f as an argument, and it returns a new function that replaces f. The same effect could be produced less concisely by writing this:

All the @ notation does is to make the syntax more concise.

See Python Decorators At Work

Here's a simple example whose output illustrates how a decorator works.

When you run the example, the output will look like this:

Note that the decorator (wrap_with_prints) runs only once, when the decorated function is created, but the inner function (wrapped) runs each time you run func_to_decorate.

An Almost Practical Example

Python's functions are like any other Python object; you can assign them to variables, pass them as arguments in function calls, return them from other functions, put them in lists and dictionaries, and so forth. (That's what it means when people say Python has first class functions.) Decorators are a concise way of taking advantage of that fact to provide useful functionality.

For example, say we have the following code (you'll recognize it as fizzbuzz):

Then, say we wanted to log the arguments and return values of our functions for debugging. (There are better ways of debugging, and you should use those instead, but this is a useful example for us.) We could add logging statements in the function, but that would be cumbersome if we had more functions, and changing the functions being debugged is likely to introduce other errors, which will also have to be debugged. We need a generalized method that leaves the functions intact.

Enter decorators to save the day! We can write a decorator function to do our logging for us:

Then, all we need to do is add our decorator to the definition of fizz_buzz_or_number:

And running the program will produce a log file that looks like this:

Again, there are better ways of debugging, but it's a nice illustration of a possible use for a decorator.

More Advanced Uses

The decorators above are simple examples; there are more advanced possibilities available.

Multiple Decorators

You can chain decorators. For example, you could use decorators to wrap the text returned from a function in HTML tags — though I wouldn't recommend it; use a template engine instead. In any case:

Then, calling greet('world') will return:

Note that the decorators are applied in order, bottom to top; the function is first wrapped by em, and then the result is wrapped in b. Failing to put decorators in the correct order can cause confusing, difficult-to-trace errors.

Decorators with Arguments

Sometimes, you might want to pass arguments to the decorator function along with the new function to decorate. For example, you might want to abstract the HTML tag-wrapping decorators b and em in the previous example into a generic tag_wrap decorator that takes the tag as an extra argument, so we don't have to have separate decorators for every HTML tag. Unfortunately, you can't do that. However, you can do something that looks like you're passing arguments to a decorator, and has a similar effect: you can write a function that takes arguments and returns a generator function:

The result of this example is the same as that of the previous one.

Let's recap, outside to inside, what tag_wrap does. tag_wrap is a function that accepts a tag and returns a function decorator which accepts a function. When passed a function, decorator returns a function called inner that accepts a string, passes the string to the function that was passed to decorator, and wraps the result in whatever tag was passed to tag_wrap.

That is a mind-stretching series of definitions, but consider it from the point of view of a library writer: it's complicated for the person who has to writetag_wrap, but for the user of the library that contains it, it's dead simple.

Decorators in the Real World

Many writers of libraries make use of decorators to provide simple ways for library users to add complex functionality to their code. For example, the web framework Django uses a decorator, login_required, to make a view require user authentication — surely a convenient way to extend a function in such a complex way.

Another Python web framework, CherryPy, goes further with its Tools, which handle authentication, static directories, error handling, caching, and much more. Every tool CherryPy provides can be configured with a config file, by calling the tool directly, or by using decorators, some of which "take arguments", as in:

Which defines a static directory called "static", located at "/path/to/app".

Though these decorators are designed to simplify their use even for those who do not fully understand how they work, a complete comprehension of decorators will allow you to use them more flexibly and intelligently, and to create your own when appropriate, saving you development time and effort as well as easing maintenance.

  • Pingback: PySide/PyQt Tutorial: Creating Your Own Signals and Slots - Python Central

  • Pingback: PySide/PyQt Tutorial: Using Built-In Signals and Slots - Python Central

  • Indranil Sinharoy

    Thanks for the short and sweet explanation on decorators.

  • andy

    it’s a wonderful article。

    • http://jacksonc.com Jackson Cooper

      Thanks! :-)

  • wbin

    print(‘About to run %s’) % fn.__name__
    should be
    print(‘About to run %s% fn.__name__’)

    so as
    print(‘Done running %s’) % fn.__name__

    • http://jacksonc.com Jackson Cooper

      Hi there. Ah yeah, you’re right. Thanks for pointing that out! It’s been fixed.

      • wbin

        It help me understand python decorator very well.

  • Pablo Bozzolo

    Great explanations! in ‘Decorators with Arguments’ there is an small mistake, it should be

    return ‘%s’ % (tag, fn(s), tag)

  • indranil sinharoy

    Nice article.

    The last line of the paragraph just under “Decorators with Arguments” says: “you can write a function that takes arguments and returns a generator function”

    I was wondering if you it should be “returns a decorator function” instead of “returns a generator function”??

  • Adarsh

    It was a very nice article and i could understand it easily :-) Thanks.

  • linh

    great article! Thank you so much!