Python Higher-Order Functions and Decorators
In Python, functions are treated as first class objects, allowing you to perform the following operations on functions.
- A function can take one or more functions as arguments
- A function can be returned as a result of another function
In this tutorial, you will learn:
1. Handling functions as arguments
2. Returning functions as the return value from other functions
3. Using closures and decorators
Functions as arguments
You can pass functions as one of the arguments to another function. This is shown in the following example.
def summation(nums): # normal function
return sum(nums)
def main(f, *args): # function as an argument
result = f(*args)
print(result)
if __name__ == "__main__":
main(summation, [1,2,3]) # output 6
The main function took in the function summation
as an argument. The main
function is a normal function which executes the supplied function with the arguments. You can see that the output reflects that. This opens up possibilities where you can pass different functions to a function, and the passed function only will be considered.
Having a function as a return value
def add_tw0_nums(x, y): # normal function which returns data
return x + y
def add_three_nums(x, y, z): # normal function which returns data
return x + y + z
def get_appropriate_function(num_len): # function which returns functions depending on the logic
if num_len == 3:
return add_three_nums
else:
return add_tw0_nums
if __name__ == "__main__":
args = [1, 2, 3]
num_len = len(args)
res_function = get_appropriate_function(num_len)
print(res_function) # <function add_three_nums at 0x7f8f34173668>
print(res_function(*args)) # unpack the args, output 6
args = [1, 2]
num_len = len(args)
res_function = get_appropriate_function(num_len)
print(res_function) # <function add_tw0_nums at 0x7f1630955e18>
print(res_function(*args)) # unpack the args, output 3
Here, you can see that different functions were returned depending on the argument in the “get_appropriate_function.”
Python closures
Let’s now look at how closures are created in Python. A closure is a way of keeping alive a variable even when the function has returned. So, in a closure, a function is defined along with the environment. In Python, this is done by nesting a function inside the encapsulating function and then returning the underlying function.
def add_5():
five = 5
def add(arg): # nesting functions
return arg + five
return add
if __name__ == '__main__':
closure1 = add_5()
print(closure1(1)) # output 6
print(closure1(2)) # output 7
Python decorators
Now, using the these ways of writing, Python code can be used to create decorators. Python decorators are convenient ways to make changes to the functionality of code without making changes to the code. A decorator is written as a function closure and implemented by giving the “@” operator on top of the function. The skeleton of a Python decorator is shown below.
def my_decorator(f):
# write your code here or your wrapping function
# return the wrapping function
pass
@my_decorator
def my_code(args):
# original functionality
pass
my_code(args)
Using this skeleton as an example, giving the @ operator on top of the function is the same as writing my_decorator(my_code(args)).
You can look at the following example where you have a function that returns a dictionary.
def my_code(args):
return {lang: args}
Executing this code will give the normal result.
>>> def my_code(args):
... return {"lang": args}
...
>>> print(my_code("python"))
{'lang': 'python'}
>>>
Say, you want to build the additional functionality of checking if the return value is a dict. In this case, you can use a decorator.
>>> def check(f):
... def wrapper(*args, **kwargs):
... res = f(*args, **kwargs)
... if isinstance(res, dict):
... print("checked that the return value is dict")
... return res
... return wrapper
...
>>>
>>>
>>> @check
... def my_code(args):
... return {"lang": args}
...
>>> print(my_code("python"))
checked that the return value is dict
{'lang': 'python'}
Here, the function “check” is used as a decorator, and it enforces the additional functionality of checking if the return of the function ”my_code” is a decorator or not.