on Sep 22nd, 2008Python decorators explained

Python decorators is just syntactic sugar to something that has been possible to do in python since version 1.0, for more background/technical information on decorators check out this pep.

The first and most important thing to remember about decorators is this: Whatever that is returned from the decorator function will replace the original function you’re decorating. Let’s start with the simplest of all decorators:

def foo(func):
	print "Decorating %s" % func.__name__
	return func

@foo	# foo is *not* called
def bar():
	print "bar"

bar()

This decorator does very little, it takes the function we’re decorating as it’s first argument, prints the name of it and then returns it, and because it returns the original function *nothing* happens except that the message Decorating bar is printed when we run the code above and when calling bar() at the last line we will get bar printed also.

As the one comment in the code above says note that foo is *not* called, the @ operator means “pass this function as the decorator for the function below” so if we only pass our “foo” function in, the calling of it is handled internally in python.

Lets step it up a notch and replace the original function, here’s the code:

def foo(func):
	def inner():
		print "Bar is gone"
	return inner

@foo # Not called
def bar():
	print "bar"

bar()

Our foo-function is a bit more advanced here since it contains an inner function, so what happens here? @foo still tells python that foo should be invoked as the decorator for bar, which it is - but this time foo returns the function called “inner” instead of the original function (which still is passed as the “func”-argument to foo, we just don’t do anything with it), so when we try to call bar on the last line we will get Bar is gone instead. This is because what I said earlier: Whatever is *returned* from the decorator will take the original functions place, so our bar() function is actually gone now and inner() has taken it’s place.

I guess most of you have seen decorators that take arguments, such as:

@decorator("arg1", "arg2")
def some_function();
	pass

These “decorators” are actually normal functions that you call with arguments, that returns decorators that are then called by python internally, here’s a real example:

def foo(string):
	def decorator(func):
		def replacement(times=1):
			print string*times
		return replacement
	return decorator

@foo("hi ho ") # Called
def bar():
	print "bar"

bar()
bar(5)

So when we call foo(”hi ho “) here, the foo function is *actually* called here and returns a decorator that takes the original decorated function as its only argument, this decorator returns a function called “replacement” that takes one argument with a default value of 1. The replacement function that is returned from the decorator replaces the original bar() function, so when we call it the first time we get
hi ho and when we call it the second time with 5 as the argument, we will get: hi ho hi ho hi ho hi ho hi ho .

The important thing to notice in the last example here is that foo is NOT a decorator here, it’s a function that takes one argument (in this case “hi ho “) and returns a decorator (aptly named “decorator” here).

3 Responses to “Python decorators explained”

  1. rajaon 23 Sep 2008 at 6:22 am

    complete, sweet.

  2. Caseyon 25 Sep 2008 at 12:01 am

    i’ve been reading about decorators for the past week…what are they good for…why couldn’t you just incorporate the decorator into the def code?

  3. ted shearon 03 Nov 2008 at 7:59 pm

    Suppose you had another nested level of a function. Would that function become the decorator? What would the calling sequence be?

Trackback URI | Comments RSS

Leave a Reply