Delving into Python Decorators: Part 1
This is part one of the four-part series on decorators in Python.
Decorators are a powerful Python feature for multiple reasons. They allow us to extend the behavior of a Callable[1] without permanently modifying them. They improve readability, maintainability, and thereby productivity. Aside from that, decorators have more practical uses in the modern programmer’s toolbox. You can use them to log functions, which can be helpful when debugging, or to time functions, to see how long it takes a function to execute. These are also examples that will be touched upon in more detail in the next few articles in the this series.
Decorators can only be used on callables. Callables in Python include Classes, Functions, and Methods [2]. Taking functions as an example, decorators allow us to execute code before and or after the decorated function without directly altering the function itself.
As mentioned earlier, It’s important to note that you can use a function as a decorator as is since it is a callable. However, you must make a class callable to use a decorator, as they are not by default [3]. This can be done by adding a __call__()
method in the class.
Below is an example of a function being decorated.
The above code can also be written as:
test_function = test_decorator(test_function)
or modified_function = test_decorator(test_function)
.
In the first instance, the original function is modified. The second case, however preserves the test_function
as is. This way, one can use test_function()
when you need to, and use modified_function()
when appropriate [4].
Now, let’s take a look at a more concrete example:
While this may seem more complicated than the last example, it really isn’t. One possible question could be “Why is the innerFunc()
being used?". The innerFunc()
is being used to wrap the test_function()
, by calling it in between the print statements. See the output:
As you can see, the decorator is working quite well. Now you could argue that it would take less work to just input the print statements into the test_function(), but in the long run, you can reuse the test_decorator with more ease, and organization. For example:
Finally, we see the versatility of decorators. Instead of adding more print statements, we simply added @test_decorator
to the beginning of the function, which produced the following output:
Note: The empty print statement was added to separate the two functions output.
In conclusion, decorators can be used to vastly improve the organization, readability and efficiency of your code. So, next time you see the opportunity to utilize a decorator instead of putting repetitive lines in your function, give it a try. You might just like the feeling of ease it brings. If you liked this article, be on the lookout for part 2 and more in the series. In the next article, we will be covering logging decorators and how to create one. Thanks for reading, and stay tuned for more!