So what exactly is a decorator?
From the python wiki:
A decorator is the name used for a software design pattern. Decorators dynamically alter the functionality of a function, method, or class without having to directly use subclasses or change the source code of the function being decorated.
To put it simply though, a decorator allows you to execute code before and after a function is called, effectively wrapping the function around other code, dynamically.
For a more practical example, lets using a simple, common task that a python developer would use, timing a function. Without the use of decorators there are multiple ways you can do this, most involve duplicating code. If you want to time a lot of functions, you could be potentially duplicating many lines of code and altering your original source code. Adding and removing the timing functionality could be a very tedious task at the end of the day.
This is what I see timing code most commonly look like in developers who do not know about decorators:
start_time = time.time()
end_time = time.time()
Now while the duplication of code isn’t too horrible, imagine if you have hundreds of functions and want to time all of them, you’d have to add that code to all functions you want to time. Quite a daunting task on a large script.
How do I use decorators?
Setup for decorators is actually really simple. Setup consists of two parts, the decorator function and adding the decorator to the function.
Keeping with the timing example above, the function for a decorator is quite simple. The decorator is a function that takes a function as an argument. Inside of that function is another function that does the wrapping. Using the timing example, this is what a decorator function would look like:
def wrapper(*arg, **kwds):
# Start the time
t1 = time.time()
# Run the function with the same arguments passed in to the original function
res = func(*arg, **kwds)
# Stop the time
t2 = time.time()
# Tell me how long it took
print '%s took %0.3f s' % (func.func_name, (t2-t1))
Now that we have our decorator function, we simply need to decorate the functions which we want to wrap. The python phrase for this is @
So there you have it, you can now decorate all the functions you want. No matter where the function is called from, python will dynamicaly alter the execution and run print_timing instead.
The issue of decorators was actually brought up to resolve an issue with OpenGL effecting the redraw of WX elements that were being dynamically updated. A decorator function was used to solve the issues, when I had mentioned the solution to a collegue of mine, he had never heard of decorators before, which brought up this whole post.