Python Decorators
A decorator takes in a function, adds some functionality and returns it. In this tutorial, you will learn how you can create a decorator and why you should use it.
Decorators in Python
Python has an interesting feature called decorators to add functionality to an existing code.
This is also called metaprogramming because a part of the program tries to modify another part of the program at compile time.
Prerequisites for learning decorators
In order to understand about decorators, we must first know a few basic things in Python.
We must be comfortable with the fact that everything in Python (Yes! Even classes), are objects. Names that we define are simply identifiers bound to these objects. Functions are no exceptions, they are objects too (with attributes). Various different names can be bound to the same function object.
Here is an example.
Output
When you run the code, both functions first
and second
give the same output. Here, the names first
and second
refer to the same function object.
Now things start getting weirder.
Functions can be passed as arguments to another function.
If you have used functions like map
, filter
and reduce
in Python, then you already know about this.
Such functions that take other functions as arguments are also called higher order functions. Here is an example of such a function.
We invoke the function as follows.
Furthermore, a function can return another function.
Output
Here, is_returned()
is a nested function which is defined and returned each time we call is_called()
.
Finally, we must know about Closures in Python.
Getting back to Decorators
Functions and methods are called callable as they can be called.
In fact, any object which implements the special __call__()
method is termed callable. So, in the most basic sense, a decorator is a callable that returns a callable.
Basically, a decorator takes in a function, adds some functionality and returns it.
When you run the following codes in shell,
In the example shown above, make_pretty()
is a decorator. In the assignment step:
The function ordinary()
got decorated and the returned function was given the name pretty
.
We can see that the decorator function added some new functionality to the original function. This is similar to packing a gift. The decorator acts as a wrapper. The nature of the object that got decorated (actual gift inside) does not alter. But now, it looks pretty (since it got decorated).
Generally, we decorate a function and reassign it as,
This is a common construct and for this reason, Python has a syntax to simplify this.
We can use the @
symbol along with the name of the decorator function and place it above the definition of the function to be decorated. For example,
is equivalent to
This is just a syntactic sugar to implement decorators.
Decorating Functions with Parameters
The above decorator was simple and it only worked with functions that did not have any parameters. What if we had functions that took in parameters like:
This function has two parameters, a and b. We know it will give an error if we pass in b as 0.
Now let's make a decorator to check for this case that will cause the error.
This new implementation will return None
if the error condition arises.
In this manner, we can decorate functions that take parameters.
A keen observer will notice that parameters of the nested inner()
function inside the decorator is the same as the parameters of functions it decorates. Taking this into account, now we can make general decorators that work with any number of parameters.
In Python, this magic is done as function(*args, **kwargs)
. In this way, args
will be the tuple of positional arguments and kwargs
will be the dictionary of keyword arguments. An example of such a decorator will be:
Chaining Decorators in Python
Multiple decorators can be chained in Python.
This is to say, a function can be decorated multiple times with different (or same) decorators. We simply place the decorators above the desired function.
Output
The above syntax of,
is equivalent to
The order in which we chain decorators matter. If we had reversed the order as,
The output would be:
Last updated