Thursday, January 27, 2011

Decorators in Python

There is a nice simple post about Python decorators from Bruce Eckels (here; discussion on Stack Overflow here, docs here, more here). I copied some of his example code and show it below, but first, the output of the run:


> python script.py 
init
init
start
Entering func1
inside func1()
Exited func1
Entering func2
inside func2()
Exited func2


The main point is that a decorator is a kind of macro for functions. It changes them, and can do so in almost unlimited ways. The output above shows that the decorator code can be executed before or after the function itself is called. (And he notes that you don't even have to do that!) They're used quite a bit in PyObjC (e.g. @objc.signature('v@0:@8')). I should probably explore all of that in another post.

As Bruce says:


Decorators allow you to inject or modify code in functions or classes

The only constraint upon the object returned by the decorator is that it can be used as a function -- which basically means it must be callable. Thus, any classes we use as decorators must implement __call__.

What should the decorator do? Well, it can do anything but usually you expect the original function code to be used at some point. This is not required, however.




class entryExit(object):

def __init__(self, f):
print 'init'
self.f = f

def __call__(self):
print "Entering", self.f.__name__
self.f()
print "Exited", self.f.__name__

@entryExit
def func1():
print "inside func1()"

@entryExit
def func2():
print "inside func2()"

print 'start'
func1()
func2()