Decorators unplugged in Python
Posted On April 4, 2008 by Sneha Latha filed under Programming
Decorators is a new idea in Python. It was in discussion for some time, but the real impact of decorators was seen in the new Python 2.4. As I write these words, several developers across the world are putting together the patches for Python 2.5 final release. In this article, I am discussing Decorators from the perspective of Python 2.4.
In the earlier article, we saw the first glimpse of decorators. The real inspiration behind introducing decorators in Python is a need to reduce the number of complex code, and to avoid duplication. An immediate need was to avoid the explicit declaration classmethod() and staticmethod.
Prior to Python 2.4, developers used declare a staticmethod like this
class A:
def foo( name):
print name
staticmethod(foo)
Imagine, this is happening in a code file of 5000 lines of code, you are likely to miss out on the staticmethod.
Hence @staticmethod is more figurative, and makes the code clearer to a code reviewer.
This is of course a minor reason. The major reason is to avoid duplication of code through decorators. Just in case, you have not noticed, the @staticmethod implies that the function foo is mentioned just once in the code.
A decorator is a callable object (like a function) that accepts one argument—the function being decorated. The return value of the decorator replaces the original function definition. In certain ways, decorators can also be used to override whole, or portions of a function.
Let us see the simplest example of a decorator. Consider this code.
def myfunc():
print "Hello Universe"
print myfunc
myfunc.py
This, if saved into a file and executed, will give an output more, or less like given below:
<function myfunc at 0xb7d7df44>
Let us modify the code given below, and call it decorator.py. As you note, this file has a new decorator function, which accepts a function, and simply returns a string ("Hello World"). We decorate the function myfunc with decorator.
def decorator(func):
return " Hello World"
@decorator
def myfunc():
print " Hello, Universe"
print myfunc
decorator.py
Executing the above file, you will get an output
>>> Hello World
What has happened? The decorator calls the function passed as argument, and wraps itself around it. Hence the function, which wraps around the function is called.
To understand decorators better, let us modify the code a bit.
def decorator(func):
return "Hello World"
@decorator
def myfunc():
return " Hello, Universe"
myfunc()
dec1.py
Execute the file dec1.py
Oops, you get an error.
Traceback (most recent call last):
File "dec1.py", line 7, in ?
myfunc()
TypeError: 'str' object is not callable
Analyzing the error, we can infer that the function myfunc has already been decorated by the time the function is called. When the function myfunc is called, Python interpreter recognizes that it is being called by a decorator. Since decorator returns a string, it says strings cannot be called, or is not callable.
Decorators can be a class
It is not imperative that a decorator need to be a function. It can be a class. The simple program, classdecorator.py as given below, best demonstrates this.
class decorator:
def __init__(self,func):
self.func = func
def __call__(__self,*__args,**__kw):
print "Hello, Universe"
@decorator
def hellow():
print "Hello, world!"
hellow()
classdecorator.py
The classdecorator.py if executed, will execute the wrapper function inside the class decorator instance. Hence the output will be Hello Universe, and not Hello World!
Note that: the decorator uses the __call__ function inside wrapper class.
Stacking Decorators
You can stack decorators one on another. The outer decorator will wrap each inner decorator. Since decorators pass values, the parameters passed (or returned by each decorator function) has to correspond to the type of argument passed by the wrapper. Sounds Confusing! Let us look at an example.
def dec1(func):
return "Hello World"
def dec2(str):
return str.swapcase()
@dec2
@dec1
def myfunc():
return "Decorators are mighty confusing"
print myfunc
stackdec.py
In this example, we have two function dec1 and dec2, which act as decorators. The first one simply returns a string and in this case, "Hello World". The second function accepts a string, and swaps its case.
The function dec1 is wrapped around myfunc as the innermost decorator. This function returns a string. The string is accepted as an argument by the function dec2, which is the outermost wrapper. The result is as given below
>>> hELLO wORLD
Because the inner function definition is executed each time the outer function is called, Python actually creates a new wrapper function object each time. Such function objects are called "lexical closures," because they enclose a set of variables from the lexical scope, where the function was defined.
A closure does not actually duplicate the code of the function. It simply encloses a reference to the existing code, and a reference to the free variables from the enclosing function. In this case, that the wrapper closure is essentially a pointer to the Python bytecode making up the wrapper function body, and a pointer to the local variables of the traced function during the invocation, when the closure was created.
Because a closure is really just a normal Python function object (with some predefined variables), and because most decorators expect to receive a function object, creating a closure is perhaps the most popular way of creating a stackable decorator.
Using Function Attributes
Check the example below
def mydecorator(name):
def decorator(func):
func.name = name
return func
return decorator
@mydecorator("Sachin Tendulkar")
def newfunc(arg1):
return arg1
print newfunc.name
In the above example, we have used the function attribute concept seen in Python for many years. The wrapper function accepts a string as an argument, and uses an inner function to return the string.
Inside TurboGears
TurboGears is a RAD framework, which has become popular in the past few months. Here is a code snippet from TG tutorial. This is an example of a function attribute wrapper.
@expose("wiki20.templates.page")
def index(self, pagename="FrontPage"):
page = Page.byPagename(pagename)
content = publish_parts(page.data, writer_name="html")["html_body"]
return dict(data=content, page=page)
This is a quick introduction to decorators. I hope this has helped you understand the nuances of a powerful idea that Python has used.
In the earlier article, we saw the first glimpse of decorators. The real inspiration behind introducing decorators in Python is a need to reduce the number of complex code, and to avoid duplication. An immediate need was to avoid the explicit declaration classmethod() and staticmethod.
Prior to Python 2.4, developers used declare a staticmethod like this
class A:
def foo( name):
print name
staticmethod(foo)
Imagine, this is happening in a code file of 5000 lines of code, you are likely to miss out on the staticmethod.
Hence @staticmethod is more figurative, and makes the code clearer to a code reviewer.
This is of course a minor reason. The major reason is to avoid duplication of code through decorators. Just in case, you have not noticed, the @staticmethod implies that the function foo is mentioned just once in the code.
A decorator is a callable object (like a function) that accepts one argument—the function being decorated. The return value of the decorator replaces the original function definition. In certain ways, decorators can also be used to override whole, or portions of a function.
Let us see the simplest example of a decorator. Consider this code.
def myfunc():
print "Hello Universe"
print myfunc
myfunc.py
This, if saved into a file and executed, will give an output more, or less like given below:
<function myfunc at 0xb7d7df44>
Let us modify the code given below, and call it decorator.py. As you note, this file has a new decorator function, which accepts a function, and simply returns a string ("Hello World"). We decorate the function myfunc with decorator.
def decorator(func):
return " Hello World"
@decorator
def myfunc():
print " Hello, Universe"
print myfunc
decorator.py
Executing the above file, you will get an output
>>> Hello World
What has happened? The decorator calls the function passed as argument, and wraps itself around it. Hence the function, which wraps around the function is called.
To understand decorators better, let us modify the code a bit.
def decorator(func):
return "Hello World"
@decorator
def myfunc():
return " Hello, Universe"
myfunc()
dec1.py
Execute the file dec1.py
Oops, you get an error.
Traceback (most recent call last):
File "dec1.py", line 7, in ?
myfunc()
TypeError: 'str' object is not callable
Analyzing the error, we can infer that the function myfunc has already been decorated by the time the function is called. When the function myfunc is called, Python interpreter recognizes that it is being called by a decorator. Since decorator returns a string, it says strings cannot be called, or is not callable.
Decorators can be a class
It is not imperative that a decorator need to be a function. It can be a class. The simple program, classdecorator.py as given below, best demonstrates this.
class decorator:
def __init__(self,func):
self.func = func
def __call__(__self,*__args,**__kw):
print "Hello, Universe"
@decorator
def hellow():
print "Hello, world!"
hellow()
classdecorator.py
The classdecorator.py if executed, will execute the wrapper function inside the class decorator instance. Hence the output will be Hello Universe, and not Hello World!
Note that: the decorator uses the __call__ function inside wrapper class.
Stacking Decorators
You can stack decorators one on another. The outer decorator will wrap each inner decorator. Since decorators pass values, the parameters passed (or returned by each decorator function) has to correspond to the type of argument passed by the wrapper. Sounds Confusing! Let us look at an example.
def dec1(func):
return "Hello World"
def dec2(str):
return str.swapcase()
@dec2
@dec1
def myfunc():
return "Decorators are mighty confusing"
print myfunc
stackdec.py
In this example, we have two function dec1 and dec2, which act as decorators. The first one simply returns a string and in this case, "Hello World". The second function accepts a string, and swaps its case.
The function dec1 is wrapped around myfunc as the innermost decorator. This function returns a string. The string is accepted as an argument by the function dec2, which is the outermost wrapper. The result is as given below
>>> hELLO wORLD
Because the inner function definition is executed each time the outer function is called, Python actually creates a new wrapper function object each time. Such function objects are called "lexical closures," because they enclose a set of variables from the lexical scope, where the function was defined.
A closure does not actually duplicate the code of the function. It simply encloses a reference to the existing code, and a reference to the free variables from the enclosing function. In this case, that the wrapper closure is essentially a pointer to the Python bytecode making up the wrapper function body, and a pointer to the local variables of the traced function during the invocation, when the closure was created.
Because a closure is really just a normal Python function object (with some predefined variables), and because most decorators expect to receive a function object, creating a closure is perhaps the most popular way of creating a stackable decorator.
Using Function Attributes
Check the example below
def mydecorator(name):
def decorator(func):
func.name = name
return func
return decorator
@mydecorator("Sachin Tendulkar")
def newfunc(arg1):
return arg1
print newfunc.name
In the above example, we have used the function attribute concept seen in Python for many years. The wrapper function accepts a string as an argument, and uses an inner function to return the string.
Inside TurboGears
TurboGears is a RAD framework, which has become popular in the past few months. Here is a code snippet from TG tutorial. This is an example of a function attribute wrapper.
@expose("wiki20.templates.page")
def index(self, pagename="FrontPage"):
page = Page.byPagename(pagename)
content = publish_parts(page.data, writer_name="html")["html_body"]
return dict(data=content, page=page)
This is a quick introduction to decorators. I hope this has helped you understand the nuances of a powerful idea that Python has used.

Gunasekharan PJ commented, on April 11, 2008 at 12:55 a.m.:
Why is Python so good a language? Why nopt Java?