This article is part of in the series
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.

About The Author