Path: blob/master/python/decorators/decorators.ipynb
2577 views
Decorators
Basic concepts:
In python, functions are
First class functionsallow us to treat functions like any other object, so for example: we can pass functions as arguments to another function, return functions and we can assign functions to variables.Closuresallows us to take advantage ofFirst class functionsthat returns an inner function that remembers and has access to variables local to the scope in which they were created.
More detailed description in the following link. Youtube: Programming Terms: First-Class Functions.
Decorators Example
A decorator is basically a function that takes another function as an argument, adds some kind of functionality and then returns another function. So why would we want to do something like this? Well, it's because this allows us to easily add or alter the functionality to our existing function method or class without having to directly use its subclasses. In short, decorators are simply wrappers to existing functions.
In the example below, the p_decorate function takes another function as an argument and generates a new function which augments the work of the original function, and returning the generated function so we can use it anywhere else.
And in python, there's neat shortcut for that, which is to mention the name of the decorating function before the function to be decorated. and perpend the decorator with an @ symbol.
So instead of calling p_decorate(get_text). It becomes:
Using multiple decorators. Note the ordering matters.
We can build decorators for class's methods in a similar fashion. We can do this by putting *args and **kwargs as parameters for the wrapper, then it can accept any arbitrary number of arguments and keyword arguments. This approach will make our decorators work for both functions and methods alike.
Looking back at the example above, you can notice how redundant the decorators in the example are. 3 decorators, div_decorate, p_decorate, strong_decorate each with the same functionality but wrapping the string with different tags. We can definitely do much better than that. Why not have a more general implementation for one that takes the tag to wrap with as a argument? Yes please!
In this case, our decorators expect to receive an additional argument, that is why we will have to build a function that takes those extra arguments and generate our decorator on the fly.
At the end of the day decorators are just wrapping our functions, in case of debugging that can be problematic since the wrapper function does not carry the name, module and docstring of the original function.
Fortunately, python includes the functools module which contains wraps. Wraps is a decorator for updating the attributes of the wrapping function(func_wrapper) to those of the original function(get_text). This is as simple as decorating func_wrapper by @wraps(func). Here is the updated example:
Practical Use Cases
Adding timing and logging to functions.
Both time and log the function
The good thing about writing the timing and logging function as decorators is that we can use them without having the re-write the logic every time we wish to use timing or logging functionality.