-
12 April 2010, 11:28
Python Decorators
Decorators are a powerful feature of Python. It's one of the reasons I love the language. If you come from a Java background, decorators will look like annotations. But there is a big difference, Python decorators are executable functions! They are basically wrappers around the decorated function.
You might be thinking what the point of this is. Well, readability matters. Take a Django site for instance; hell, let's use mine. Some features are only accessible if the user is logged in. So I decorate my view function with
@login_required. The code to check whether or not a user is logged in is as boilerplate as it gets. Check if the current user is logged in; if he or she is, continue; otherwise, return "Authorization Required". Thelogin_requireddecorator does this. Even if you called a function that checks for login inside your view function, it still wouldn't look nearly as clean. With decorators like this, you just go on assuming log in worked, and let the decorator handle any errors.Implementing decorators is just as simple as reading them, but it requires you to understand functions as objects. Basically, functions can be passed around like any other object. Here's the code for a simple example:
def dec(f): def inner_func(*arg, **kwargs): f(*arg, **kwargs) print "inner_func" return inner_func @dec def func(): print "func" func()In the above example,
decis declared like any other function. One special thing about it though: it declares an inner function that it later returns. This inner function is responsible for executing (or not executing) the original function that was passed as an argument to the decorator function. As you can see, the inner function takes*argsand**kwargsas parameters. These are variable length arguments and keyword arguments, respectively. Those are necessary so you maintain the same method signature. I won't go into what those mean here; maybe I will in a later post.This might look like magic, but it's really just syntactic sugar. The above example is equivalent to:
def dec(f): def inner_func(*arg, **kwargs): f(*arg, **kwargs) print "inner_func" return inner_func def func(): print "func" func = dec(func) func()Decorators can certainly be chained, resulting in something like:
@d1 @d2 def func(): passAnd decorators can also take additional arguments, but I think this is much less readable (and useful).
def dec(arg): print arg def inner_func(f): print "inner_func" def nested(*args, **kwargs): f(*args, **kwargs) print "nested" return nested return inner_func @dec("decorator argument") def func(): print "func" func()In this article, you learned how Python decorators can be useful for extending the functionality of your functions, while keeping your code readable. I hope you enjoy this feature of Python as much as I do.