A Guide to Python Decorator
In this blog, I am going to walk you through python decorator, which will make your code more concise and more professional.
Lets start by creating the simplest possible decorator
1 | def simplest_decorator(func): |
The program above only does one thing: print a message before actually calling the function itself, this is similar to the following (but you will see the difference later):
1 | wrapped_function = simplest_decorator(no_arguments) |
Ok, the example above is quite simple but it is useless since we are not really “decorating” the function passed in per se. We just returned it as is. So we need to do more:
1 | def still_simple_decorator(func): |
Now our decorator becomes a little bit more complicated but it is still not hard to figure out what it is doing. Instead of returning the original function as is, we create a inner function taking position and keyword arguments.
Now we are more flexible, we can truly wrap our function inside. Think about the following use case: we need to time the run time of a function, this involves starting the timer at the top of the function then stop and calculate the time difference at the end of the function. With decorator, you can do it more elegantly without having to plug in code not related to the logic of the function itself
1 | from time import time |
THe above is useful, whenever we want to profile a function, the only thing we need to do is to decorate the function with @time_it decorator, and you will find the run time!
However, above still have a problem. Sometimes, we need to use function properties, such as __name__
or __doc__
if we do it on the wrapped function:
1 | print test_time_it.__name__ |
The print out is not what we expect, for name is should be test_time_it
instead of wrapper
! To solve this problem, we need to use another decorator for functools library, just decorate your wrapper with @wraps(func) like bellow
1 | from functools import wraps |
Try the print we just did, it should work now.
Well, we have tried decorator on functions, and I bet you already know that decorator is merely a syntax sugar that helps you wrap your function object into another function. This another function can also take arguments like all other functions
1 |
|
This is a little bit more complicated, now our decorator becomes a function that takes arbitrary number of position arguments. To use these arguments, we create a closure. The function decorator
and its inner function wrapper resemble our previous decorators: it takes the function as argument. Since everything is within the closure formed by decorator_takes_arguments
, we can use its argument freely.
You can wrap as many level as you like but how about using a class as decorator, since decorator is only a function that takes function as argument.
1 |
|
Above is fancier, but the concept is the same. The beauty of using class to create decorator is that you can easily create a decorator library with nice structural syntax.
That’s it for today, I will add more topic about decorator in the future