Python decorators

November 7, 2013

In Python, functions are first class objects. This only means that, like everything else, they're objects. That can be a handy feature because that way we can pass functions as arguments to other functions. And also we can use functions as return values of other functions. Confusing, uh? Let's see that with an example.

def func_received():
print "I was received"
def receiver(new_func):
def return_function():
def func_to_return():
print "I was returned"
return func_to_return
func_returned = return_function()

If we run this, without any surprise we get:

$ python example.py
I was received
I was returned

As you can see we can use functions as an argument or return value, as everything else. So what's this all about decorators? Well, they're simply callables that can take a function as argument and return a replacement function as a result. Let's see some code.

def dec(func):
def inner_func(*args, **kwargs):
res = func()
return res + " and I was decorated!"
return inner_func
def func():
return "I am a function"
decorated = dec(func)
print decorated()

Again, by running, we get:

$ python decorators.py
I am a function and I was decorated!

This can be quite a handy feature. Let's imagine a web application where you want to be sure that some action only takes place if a user is logged in. You can always do that verification right there, but this way you have a simple an easy form to achieve that. The only thing that's required, is a decorator function that does exactly that.

In fact, it would be very nice if we had some piece of syntactic sugar to help us. And since Python 2.4 we have!. The @ applies a decorator to a function. Cool, uh? Some code to make it clearer.

def dec(func):
def inner_func(*args, **kwargs):
number = args[0] * 10
res = func(number)
return res + " and I was decorated!"
return inner_func
def func(number):
return "I am a function %d" % number
print func(1)

Now it has got a lot better and clear. But as an astute reader, you notice those intruding *args and **kwargs. An just to to exemplify that we can do something with them, I multiplied it by 10. Well, it wouldn't be very helpful if we couldn't get the input of our decorated functions, would it? So, those arguments are:

  • *args stores positional arguments
  • kwargs stores all uncaptured keyword arguments. This simply means that kwargs does for dictionaries and key/value pairs exactly what *args does for iterables and positional parameters.

The names args and kwargs are not part of Python syntax, but they're a useful convention.

As we can see, decorators can quite a handy feature. If you use, for example Django, you will find them flying around and now you have an idea of what's going on.

Ricardo Castro

Ricardo Castro

Software Engineering, DevOps, Taekwondo and Metal



© 2021