Mar
10

# Function/Method Decorators

By Eric Shull  //  Python  //  No Comments

One of the reasons I like Python is because it’s simple but powerful. All the tools are there for whatever task needs to be done. I’ve found __getattr__ to be a very useful tool for creating sets of standardized class methods without having to explicitly declare them. Another useful tool is function decorators. Read on to get a quick idea of how to make your own class decorators.


@

Function decorators are simply functions that take as their argument another function. They are a little tricky to construct, but if you’re familiar with passing functions around as variables, it will be fairly easy to understand.

Here is a simple function that prints the name of the function it decorates:

def give_name(func):
    print "My name is", func.__name__
    return func

To make this function decorate another function, use the @ operator before the function definition.

@give_name
def a_function(data):
    print data

When you call a_function, it really calls give_name because the @ function decorates the function that is actually called. When you call give_name, it prints the name of the func variable then returns func. The function that was passed into give_name was a_function, because that was the function definition that came immediately after @give_name, the decorator.

Why?

You might be wondering why anyone would ever want to do something so convoluted. In this case, you can add a single line above a function definition if you want it to print its name when it is run. You can add the same decorator to as many functions as you want. When you call the function, they’ll display their name, then run. This is especially helpful for debugging, such as when the programmer wants to see whether a function is actually being called anywhere.

I’ve found a lot of use for another decorator that I call time_this, a form of the benchmark decorator found here. The code is below.

import time

def time_this(func):
    def wrapper(*args):
        t1 = time.clock()
        result = func(*args)
        t2 = time.clock()

        print("%s: %0.3f ms" %(func.__name__, (t2-t1)*1000.0))
        return result

    return wrapper

If I want to time the performance of a function, I just have to add @time_this before the function definition. Then whenever that function is called, the amount of time it takes running is printed out. This helped me identify some key bottlenecks in a simulation I wrote for a research project. In the end I was able to cut the running time to a third of what it was.

See if you can figure out what happens when this decorator is used like this:

@time_this
def a_function():
    for i in range(100):
        print i

If you have any questions, feel free to ask in the comments!

Leave a comment