Function decorators in python

This is nice python syntax sugar that may look like magic but really isn't. It can be used for quick and easy AOP, registering functions with objects... you name it.

Introduction

Python decorators are objects that are called, returning a function that wraps the function they decorate, taking the wrapped function object as an argument and replacing it with the wrapper function.

A simple decorating function can look like this:

def decorator(func):
    def wrapper(*args):
        return 'x' + func(*args) + 'x'
    return wrapper

and is used to decorate another function like this:

@decorator
def test():
    return "hello"

Calling test will now return the string "xhellox". * prefixed arguments will accept an arbitrary number of arguments, putting them into a tuple, and can be dereferenced into arguments again with * in a function call. We use this to pass all the non-keyword arguments in the wrapping function to the wrapped function. Keyword arguments can similarly be extracted from dictionaries using ** and turned back into arguments in a function call with ** again.

This is good to know if you just want a "pass-through" decorator (maybe some sort of "logging" aspect) that works for any function or method.

Dynamically creating decorators

The simple example above can be extended to support different appending different characters, but the object following the "@" in the decorator still needs to resolve to a function. What if we could decorate as such:

@decorator('$')
def test():
    return "hello"

>>> test()
'$hello$'

The decorating function can now no longer be the function you use to generate it, but the function it returns. The decorator in the example above would be a misnomer.

def decorator_factory(prefix)
    def dec(func): # the actual decorator
        def wrapper(*args):
            return prefix + func(*args) + prefix
        return wrapper
    return dec

@decorator_factory('$')
def test():
    return "hello"

>>> test()
'$hello$'

Decorators as classes or class members

A decorator can be any callable object. This includes classes and any object that implements __call__