Jason in a Nutshell

All about programming and whatever else comes to mind

The magic of python decorators

Posted by Jason Baker on April 25, 2009

Decorators in Python are one of the language’s more “magical” features.  Personally, I’ve tended to glaze over decorators in code because they always seemed to be fairly self-explanatory.  But how do you make your own?  Personally, I think understanding the uses of decorators and being able to write your own is one of the points where a python newbie transitions to a knowledgeable pythonista.

But what is a decorator?

In actuality, there’s not an actual language construct to define decorators.  Any function that takes a function as a parameter and returns one as a result may be used as a decorator.  These are known as “higher-order functions” in functional programming circles.

Chances are, you’ve already seen decorators in use and maybe even used them.  I’ll give you a common example of decorator usage:

class SomeClass(object):
     @property
     def x(self):
         return 5

>>> var = SomeClass()
>>> var.x
5

For those of you familiar with the concept of properties, it should be pretty straight-forward what’s going on here.  But where the heck did property come from?  I’ll give you a hint.  The above code works identically to this code:

class SomeClass(object):
    def x(self):
         return 5

    x = property(x)

 

I can write my own?!

Yes you can.  A lot of well-written libraries make very good use of decorators.  And given the right situation, the little bit of syntactic sugar they provide can do a lot of good.  But decorators aren’t just there for others to define.  Once you wrap your head around decorators, they can save you a lot of copy-and-pasting (which you’re not doing anyway, right?) when used in your own code.  To illustrate this, I want to show you a couple of very much real-world cases that I’ve found decorators to be useful.

Those pesky connection objects

I have a library that needs to call a few particular stored procedures in a SQL Server database.  Because my ORM doesn’t support stored procedures, I have to use straight adodbapi.  The calls look something like this:

import adodbapi

def LookupPerson():
    conn = adodbapi.connect(CONNECTION_STRING)
    try: 
        #do stuff here
    finally:
        conn.close()

This is all well and good for just one function.  But what happens when you need 4 or 5 of these?   And what about the visual cruft that the try finally block is adding to the function (adodbapi doesn’t support with blocks before you ask)?  I’m sure you’ve already guessed the solution by now.  Here’s how you can solve this problem:

import adodbapi

def with_connection(func):
    def _exec(*args, **argd):
        conn = adodbapi.connect(CONNECTION_STRING) 
        try:
             func(conn, *args, **argd)
        finally:
             conn.close
     return _exec 

@with_connection
def LookupPerson(conn):
    #do stuff here

LookupPerson()  #conn argument is passed by the decorator

 I think that the simplification that happens with LookupPerson here should be obvious.  But what is the purpose of the *args and **argd shenanigans?  The documentation covers arbitrary argument lists in depth, so I won’t go into too much detail.  But what happens if I want to lookup a person by name?  The LookupPerson function would be transformed to this:

@with_connection
def LookupPerson(conn, name):
    #do stuff here

LookupPerson('Bob')  #conn argument is passed by the decorator
LookupPerson(name='Jill')

 In fact, I can decorate any function that takes any number of arguments either by keyword or by position.  Pretty neat, eh?

Error handling

When doing web applications, it’s pretty important to have decent error handling.  But setting this up can be a pain.  For instance, what if I wanted to make my django application print a wonderfully informative traceback to a log file?  I could do that like this:

from traceback import format_exc

def index(request):
     try:
          #do stuff
     except:
         logging.log(format_exc())
         return HttpResponseServerError('Error!')

But this definitely can become problematic.  What happens if you duplicate this code in all of your view functions and you want to make a change to your error handling?  The solution is simple:

def handle_errors(func):
     def _handler(*args, **argd):
          try:
               func(*args, **argd)
         except:
                logging.log(format_exc())
                return HttpResponseServerError('Error!')

@handle_errors
def index(request):
     #do stuff here

 As you can tell, this allows us to make our views worry about actually doing stuff instead of constantly handling errors.  Yes, there are also middlewares for doing this kind of thing.  But then I wouldn’t have a reason to make a blog post about python decorators, would I?

Conclusions

Ok, so I’ll admit something.  Python’s decorator syntax is ugly.  Its Java-like syntax alone may even be enough to scare some off.  But as I’ve show, there are at least a few real-world cases where they are useful.

What are some other decorators that you’ve found to be useful?

About these ads

One Response to “The magic of python decorators”

  1. rubayeet said

    Nice article Jason. Thanks!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
Follow

Get every new post delivered to your Inbox.

%d bloggers like this: