What are decorators?
Heiko Wundram
heikowu at ceosg.de
Mon Aug 9 14:25:42 EDT 2004
Am Montag, 9. August 2004 18:58 schrieb gohaku:
> The examples I have seen written in Python of this "Not Yet
> Implemented" feature
> are confusing to say the least and perplexes me as to its usefulness.
Decorators change an object at declaration time (well, Python only supports
decorators for methods and functions atm., which are top-level objects
nonetheless). Basically, what is done when using decorators is the following:
class x(object):
@ <somedecorator>
def y(self,<someparams>):
<blah>
translates to:
class x(object):
def y(self,<someparameters>):
<blah>
y = <somedecorator>(y)
Now, somedecorator gets the method object (rather, a function object) for
method y, and may do with it whatever it wishes, needing to return a new
method object.
Thus, you might do the following:
<code python="2.4">
authors = {}
# Defining the decorator, just a function.
def func_spec(**kwargs):
"""
This decorator decorates the function with an additional attributes. These
additional attributes can be used to confer information about the current
function.
"""
global authors
def decoratef(f):
for k, v in kwargs.iteritems():
if k == "author":
if v not in authors:
authors[v] = 0
authors[v] += 1
setattr(f,k,v)
return f
return decoratef
# Defining the class x, which contains a function which has an attribute named
# author.
class x(object):
@ func_spec(author="Heiko Wundram",version="0.1")
def y(self):
print "Calling y."
@ func_spec(author="Somebody else",version="0.2")
def z(self):
print "Calling z."
# Now, for usage.
ob = x()
print "y's author and version:", ob.y.author, ob.y.version
print "z's author and version:", ob.z.author, ob.z.version
print "Authors, and functions by authors:", authors
ob.y()
ob.z()
</code>
This example is an example of a decorator facilitating attaching meta-data to
a function. The decorator also keeps track of how many functions are written
by each author (programmer), which might be useful in case you're working for
a big company or something...
Anyway, decorators as shown above already work out of the box in Python 2.3,
but with a more cumbersome syntax:
<code python="2.3">
class x(object):
def y(self):
print "Calling y."
y = func_spec(author="Heiko Wundram",version="0.1")(y)
def z(self):
print "Calling z."
z = func_spec(author="Somebody else",version="0.2")(z)
</code>
Now, decorators can't just change the function object and return it. They may
also return a completely new function object:
<code>
import sys
import traceback
# Decorator printing debugging output.
def debugger(f):
def debugf(*args,**kwargs):
print "---- DEBUGGER STARTS HERE ----"
print "Entering:", f.__name__
print "Arguments:", args
print "Keyword arguments:", kwargs
print "Running function..."
try:
try:
retv = f(*args,**kwargs)
print "Function returned:", repr(retv)
return retv
except:
print "Running function raised exception."
traceback.print_exception(*sys.exc_info())
raise
finally:
print "Leaving:", f.__name__
print "---- DEBUGGER ENDS HERE ----"
return debugf
class x(object):
@ debugger
def y(self,raise_exc=False):
if raise_exc:
raise Exception, "We raise an exception here."
else:
return (42,"the answer to everything")
ob = x()
# Call it, once without raising, once with.
ob.y()
ob.y(True)
</code>
Now, you could certainly make the debugger decorator a little more intelligent
(for example, using inspect, it could parse the arguments and print them out
properly), but this example just shows that debugging one function of your
code (rather, inspecting input and output) is as easy as just adding
@ debugger in front of the function definition. When you're done debugging the
function, just remove @ debugger.
Again, as before, this could've been done in another fashion for <=2.3
<code python="2.3">
class x(object):
def y(self,raise_exc=False):
<blah>
y = debugger(y)
</code>
But again, having the decorator in front of the function definition simply is
more beautiful, that's why this syntactic sugar is being introduced.
I've already written up several other examples (e.g. method synchronization),
which you can find all over c.l.p.
To sum it up: decorators can do a lot, and basically what they do has already
been available in Python <= 2.3, but adding syntactic sugar for decorators
makes it nicer to use them.
Heiko.
More information about the Python-list
mailing list