Decorators were introduced in Python 2.4 to provide a clean syntax for function wrappers. New syntax was not only applied to already existing Python built-in wrappers, but also enabled programmers to elegantly wrap any function or method with additional functionality.

According to the Python Glossary, a decorator is a function returning another function. This is a pretty vague description, so maybe it’s best to take a look at some code.


Next few lines describe how a decorator body looks like in general case:

def decorator_name(function):
    def wrapper(*args):
        # 1. Do some preprocessing.
        # ...

        # 2. Call 'function' with given arguments.
        function(*args)

        # 3. Do some postprocessing.
        # ...
    return wrapper

Bear in mind that the wrapper function must have the same signature as the decorated function. To be able to decorate any function, the template wrapper takes a tuple of non-keyword arguments (*args). Now, all that’s left is to actually apply the decorator:

@decorator_name
def some_func():
    pass

Keep in mind that this is equivalent to:

def some_func():
    pass
some_func = decorator_name(some_func)

So, the basic idea is simple – a decorator gives us an opportunity to do something before and after the decorated function gets called. Several simple examples are due:
1. Timing. To see how much time your functions consume, check the clock before and after the call, then print the difference:

import time

def timed(some_function):
    def wrapper(*args):
        start = time.clock()
        result = some_function(*args)
        end = time.clock()
        print some_function.func_name, end - start
    return wrapper

2. Printing and logging. Function arguments can easily be printed to console or logged to a file with decorators; same approach can be applied to return values. Decorator arguments can be used to divert logging to a different path without changing the decorator itself.
3. Debugging. You can check/assert argument values or types before the original function call. Note, however, that using isinstance() is usually discouraged (duck typing, remember?:).

Of course, this is just a tiny bit of all possible decorator uses. Make sure you check out Python Wiki for more examples and some inspiration.

0 comments