User Tools

Site Tools


python:decorators

DECORATORS

28.05.2009

What are they?

Python decorators implement the Decorator pattern, but in a limited way. They provide a way to modify of inject code into functions or classes.
The original proposal of Decorators as an enhancement and the discussion thereby are documented in PEP (Python Enhancement Proposal) 318

How to use?

To decorate a function, you just simply use @<decorator_name> one line above your function definition.

@decorator_name
def myFunction():
    print "myFunction here!"

When the code is compiled, the myFunction is compiled and then passed to decorator who produces a function-like object to substitute myFunction.

  • if we use a class as decorator, then they must implement call method (the object return by the decorator must be callable).
  • decorator class constructor is executed at the point of decoration of the function.
  • decorator constructor receives the function object being decorated (it could be use later in call)

Some examples please

#!/usr/bin/env python2.6
# -*- coding: utf8 -*-
 
 
class MyDecorator(object):
    def __init__(self, f):
        self.f = f
 
    def __call__(self, a, b):
        self.f(a,b)
        print "Running decorated function"
        print "%s * %s = %s " % (a,b,a*b)
 
 
@MyDecorator
def addFunction(a, b):
    ''' Original behaviour function adds two numbers '''
    print "Running original function"
    print "%s + %s = %s " % (a,b,a+b)
 
 
addFunction(3,4)

will return:

Running original function
3 + 4 = 7 
Running decorated function
3 * 4 = 12 

You notice, in call method, we first call the initial function, and then modified the behaviour. A slightly modified code, can use a switch to change from the old behavior to the new one.

#!/usr/bin/env python2.6
# -*- coding: utf8 -*-
 
 
class MyDecorator(object):
    def __init__(self, f):
        self.f = f
 
    def __call__(self, a, b, new = 0):
        if new == 1:
            print "Running decorated function"
            print "%s * %s = %s " % (a,b,a*b)
        else:
            self.f(a,b)
 
 
 
@MyDecorator
def addFunction(a, b):
    ''' Original behaviour function adds two numbers '''
    print "Running original function"
    print "%s + %s = %s " % (a,b,a+b)
 
 
addFunction(3,4)
addFunction(5,2)
addFunction(3,4,1)


Now, the same example using function as decorator instead of a class:

def MyDecorator(fn):
    def decorate(a, b):
        fn(a, b)
        print "Running decorated function"
        print "%s * %s = %s " % (a,b,a*b)
    return decorate
 
@MyDecorator
def addFunction(a, b):
    ''' This initial function adds two numbers '''
    print "Running initial function"
    print "%s + %s = %s " % (a,b,a+b)
 
addFunction(3,4)

Now, an interesting example (inspired from here) exemplifying parameter sent to the decorator itself. Change the parameter from * to + to see the difference; the decorator function chooses which method to use further (add- or muldecor)

#!/usr/bin/env python2.6
# -*- coding: utf8 -*-
 
def MyDecorator(operation):
    def adddecor(fn):
        def inner(a, b):
            fn(a+b)
        return inner
    def muldecor(fn):
        def inner(a, b):
            fn(a*b)
        return inner
    # adddecor and muldecor should be referenced first
    if operation == '+':
        return adddecor
    elif operation == '*':
        return muldecor
 
 
@MyDecorator('*')
def addFunction(a):
    print "Output %s " % a
 
 
addFunction(2,3)

python/decorators.txt · Last modified: 2013/03/16 17:40 (external edit)