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