Originally posted on towardsdatascience
Decorators in Python
A decorator in Python is a function that takes another function as its argument, and returns yet another function. Decorators can be extremely useful as they allow the extension of an existing function, without any modification to the original function source code.
Consider the following example:
1. add_together function
The simple add_together function takes 2 integers as arguments and returns the summation of the 2 integer values passed.
1.1. Extending the functionality of add_together with a decorator
Lets propose the following scenario. We would like add_together to be able to take a list of 2 element tuples as its argument and return a list of integers which represents the summation of their values. We can achieve this through the use of a decorator.
Firstly, we give the decorator a sensible name which intimates what its intended purpose is. Here the decorator, called decorator_list is simply a function that takes another function as its argument. This function has been named ‘fnc’ in the arguments list to decorator_list.
Inside the decoratored function, we define a local function called inner. The inner function takes a list of 2-element tuples as its argument. The inner function loops through this list of 2 element tuples, and for each tuple applies the original function, add_together, with index position 0 of the tuple being the argument to a in add_together and index position 1 being the argument to b in add_together. The inner function returns a list of these values summed up. The decorator_list function finally returns the inner function.
Now, let’s apply this logic and see how the decorator function works.
To apply the decorator, we use the syntax @, followed by the function name of the decorator above the function that is being decorated. This is syntactically the same as:
Here, we pass the function add_together to the decorator_list function, and this function returns the inner function which we assign to the variable name add_together. As add_together now points to the inner function, which expects a list of 2 element tuples, we can call add_together with a list of tuples as the argument.
The standard output to the console shows that we have now extended the functionality of the original add_together function, so that it can now take a list of tuples.
The complete source code for the decorated function is available here:
2. Decorators that can take arguments themselves
It may also be useful for decorators to take arguments themselves. In the slightly altered example presented here, we pass an integer argument to the decorator. This integer value is used as the exponent after the two values in the tuple have been added together. Here, the value 2, squares each value, so for the first tuple, 16 is returned (i.e. the square of 1 + 3).
To use arguments in decorators, we simply need to define a decorator itself. In the example below, 2 is the argument passed to the meta_decorator. This meta_decorator function returns the decorator_list function with 2 passed as the argument to power. This decorator_list decorator is then used in the ordinary way, i.e. it takes the add_together function and returns inner, which we can then call with our list of tuples.
Now we have a list of our tuples returned which have been added together and squared. To cube, or quadruple, we simply add either 3, or 4 to the argument of the meta_decorator.
The source code for this example is shown:
3. Default arguments in Decorators
Now we have seen that we can specify arguments to the decorator, we can also specify no arguments to the decorator and a default will be set.
If we do not pass any arguments to our decorator, as we are doing with @meta_decorator, args is considered a function.
As the built-in callable function with an function passed will return a boolean True, power is assigned the value of 2 and we can directly call the decorated_list function immediately which will return inner.
If arg is not a function, but an integer, it is not callable (as would happen in the code that has been commented out). We then move to the else statement which executes its corresponding block. Power is assigned whichever value we pass, we then create the decorator_list function which finally returns inner.
The source code for this code snippet is shown:
Decorators are an elegant way to extend functionality of our original functions without altering their source code. Furthermore, the decorators we define can accept arguments or fall back to a set predefined default argument. This article showcases the basics of decorators and how they may be incorporated into our function design.