Python Decorators Basics

A simple program using decorators

The following is a simple program using a decorator:

#!/usr/bin/python
# A very simple decorator demonstrating the basic concept

def timestamp():
import time
import datetime
ts = time.time()
mask = '%Y-%m-%d %H:%M:%S'
return datetime.datetime.fromtimestamp(ts).strftime(mask)

def add_time_stamp(foo):
''' add_time_stamp is a decorator accepting a function foo '''
def inner(*args, **kargs):
'''
inner is the function containing the decoration
which in this case consists of printing the timestamp
and the calling the outer function
'''
print timestamp(),
foo(*args, **kargs)
return inner

@add_time_stamp
def show_msg(msg):
'''
show_msg takes a single parameter as its argument and prints
it to the screen.

Note hat he definition of show_msg is preceeded by @add_time_stamp
something that is equivalent to the following:

def show_msg(msg): print msg
show_msg = add_time_stamp(show_msg)
'''
print msg

if __name__ == "__main__":
show_msg('test')

Running this program from the command line, you will see the following output:

2014-05-15 18:04:36 test

As we can see, we are getting the current time-stamp followed by the string we passed (‘test’ in our case) printed in the standard output. The implementation of show_msg knows nothing about the time-stamp, is the decorator that is explicitly print the time-stamp and then calls the original function.

What is interesting in this case though, is the little magic that is going on with the use of the @add_time_stamp right before the definition of show_msg. The “at” sign (@) that goes before the add_time_stamp is a syntactic sugar, saying to python that we want to use the function who’s name is printed right after it to decorate the function that will be defined in the immediately next line!

The following lines of code:

@add_time_stamp
def show_msg(msg):
print msg

are exactly equivalent to the following:


def show_msg(msg):
print msg

show_msg = add_time_stamp(show_msg)

The use of the ampersand can be viewed as a syntactic sugar that simplifies the way we are writing the code, making it less verbose and more expressive. Although it might seem like a simple facility, this mechanism very useful and in many cases changes completely the way we write code, something that will become more evident as you start using this approach more and more..

Note that our decorator will work with any function regardless of the number of parameters it takes, because the inner function is using the following signature:

def inner(*args, **kargs):

keeping itself transparent to the specific signature of the function that is been decorated.

At this point you know enough to start writing some more useful snippets that will use decorators in your programs, to see an example keep on reading the next page…

6 Comments

  1. Interesting & was well explained, but curiously – I get an Error:

    python -i Testing.py
    Python 2.7.6 (default, Mar 22 2014, 22:59:38)
    [GCC 4.8.2] on linux2 <- Lubuntu 14.04

    Traceback (most recent call last):
    File "Testing.py", line 13, in
    @handle_exceptions_ex(True)
    TypeError: ‘NoneType’ object is not callable

    #!/usr/bin/python
    import sys

    def handle_exceptions_ex(print_message):
    def handle_exceptions(foo):
    def inner(*args, **kargs):
    try: foo(*args, **kargs)
    except Exception as ex:
    if print_message: print ex
    return inner
    return handle_exceptions

    @handle_exceptions_ex(True)
    def print_file(filename):
    f = open(filename)
    print f.read()

    @handle_exceptions_ex(True)
    def divide(x,y):
    a = x/y
    return x / y

    #divid(2, 0)
    print_file(‘nonexistent_file’)

  2. Verify that the “return inner” is aligned with “def inner(*args, **kargs):”
    Although your code is not aligned properly, I think that the “return inner” is aligned one tab to the right, causing your decorated function to return None

    def handle_exceptions_ex(print_message):
        def handle_exceptions(foo):
            def inner(*args, **kargs):
                    try: 
                        foo(*args, **kargs)
                    except Exception as ex:
                        if print_message: print ex
            return inner
        return handle_exceptions
    
    

Leave a Reply

Your email address will not be published.


*