You might have heard about decorators in Python. Decorators are implemented using a closure. In this article, we will study closures in python. To understand closures in a better way, we will first study nested functions and free variables as these are prerequisites for understanding a closure in python.
What is a Nested Function?
A nested function is a function defined inside another function. By scoping rules, a nested function can access all the variables defined in its enclosing function. A nested function can use any non-local variable from its enclosing function but we cannot modify the nonlocal variable because it is in read-only mode by default. This can be understood from the following example.
def enclosing_function():
myVar = 1117
print("It is enclosing function which encloses a nested function.")
def nested_function(val):
print("I am in nested function and I can access myVar from my enclosing function's scope.")
print("The value of myVar is:", myVar)
temp = myVar + val
print("Value after adding {} to {} is {}.".format(val, myVar, temp))
nested_function(10)
# Execution
enclosing_function()
Output:
It is enclosing function which encloses a nested function.
I am in nested function and I can access myVar from my enclosing function's scope.
The value of myVar is: 1117
Value after adding 10 to 1117 is 1127.
In the above code, we have defined an enclosing function named enclosing_function. In that, we have defined a nested function named nested_function. Here, you can observe that we can access a variable declared inside enclosing_function in the nested_function.
To modify the non-local variables inside the nested function, you may declare the non-local variables using the nonlocal keyword.
What is a Free Variable?
In a program, we can access a variable only within the scope in which it is declared. In other words, If we declare a variable inside the scope of a function or a code block, it can be accessed only within that scope. If we try to access the variable outside the scope, a NameError exception will occur.
In some cases like nested functions, we can access a variable outside the scope where it is defined. Such variables that can be accessed outside the scope in which they have been declared are called free variables. In the above example, myVar is a free variable as we can access it inside the nested_function.
Now that we have understood the concept of nested functions and free variables, let us see what a closure is in Python.
What are Closures in Python?
The closure is a technique with which we can attach data to a code. To implement a closure in Python, we use free variables and nested functions. After defining a nested function inside the enclosing function, we return the nested function using the return statement. Here the nested function should use a free variable to create a closure. If the nested function is using a free variable, the nested function is called the closure function.
A closure function allows you to use a variable even if the scope in which it was defined has been removed from the memory. By using the closure function, we can access the free variable that was defined in the enclosing function even if the scope of the enclosing function has been destroyed. Thus, the data in the enclosing function gets attached to the code in the closure function.
Conditions for Closures to Exist in Python
For a closure to exist in Python, certain conditions should be satisfied. These conditions are as follows.
- There should be a nested function defined inside an enclosing function.
- The nested function should access variables from the enclosing function.
- The enclosing function should return the nested function.
Example of a Closure in Python
Having studied the concept behind closure in Python, let us look at an example to understand the concept in a better way. Following is a python program to create a function that adds a random number to the input.
import random
def random_addition():
values = [123, 1123, 11123, 111123, 1111123]
def nested_function(val):
x = random.choice(values)
temp = x + val
print("Value after adding {} to {} is {}.".format(val, x, temp))
return nested_function
# Execution
random_function = random_addition()
random_function(10)
random_function(23)
random_function(24)
Output:
Value after adding 10 to 123 is 133.
Value after adding 23 to 1123 is 1146.
Value after adding 24 to 111123 is 111147.
In the above code, we have defined an enclosing function named random_addition. In random_addition, we have a list of numbers. Inside random_addition, We have defined a nested_function which takes a number as input and adds a random number from the list of numbers defined in random_addition to the input value and prints the output.
If you will observe the code, you can see that the function random_addition is executed only once in the program and it returns the nested function which is assigned to the random_function variable. Once the execution of random_addition is completed, its scope is wiped out from the memory. But, we can execute the random_function any number of times and each time we access the values defined in the random_addition function. It happens even if the scope of random_addition is not present in the memory. This means that the data has been attached to the code which is the main function of a closure in Python.
Conclusion
In this article, we have discussed nested functions, free variables, and closures in Python. A closure is a wonderful tool to use if you want to avoid the use of global variables. Closures are also used in implementing decorators in python. To learn more about python programming, you can read this article on list comprehension. You may also like this article on the linked list in Python.
Recommended Python Training
Course: Python 3 For Beginners
Over 15 hours of video content with guided instruction for beginners. Learn how to create real world applications and master the basics.