Closures In Python | FACE Prep

In Python, the concept of closure is applicable only to Nested Functions. Closures in Python are the inner functions that have access to variables declared/initialized inside an outer function (enclosing function) even after the outer function has been executed completely or removed from the memory. 

Before learning the working of python closures, let us understand the scope of variables in nested functions. 

Scope of Variables In a Nested Function

Whenever we print any variable inside an inner function, the Python interpreter searches for that variable declaration/initialization until four scopes. First, the local scope of the inner function, then the local scope of the enclosing function, then the global scope and at last the built-in module scope. So, the inner function can access the variables declared/initialized in all these four scopes as shown in the below image.

closures in python

Keeping this in mind, let us move ahead to understand Python Closures in detail.

Understanding Python Closures

Consider the below-given nested function in which the variable ‘b’ is accessed inside the inner function. 

def outer():
    b = 20  #variable inside the outer function
    def inner():
        c = 30  #variable inside the inner function
        print(b + c)
    inner() #call to inner() function

#main() function
outer() #call to outer() function
Output:
50

Execution order:

closures in python

Here, since the inner function is called from the outer function, the Python interpreter starts the execution of the inner function before the outer function completes its execution. The above-defined function becomes closure when we return the inner function instead of calling it from the outer function. Let us see how to do that.

Defining Closures

Let us consider the same example, where we rewrite the function definition by returning the inner function from the outer function instead of calling it as shown below.

def outer():
    b = 20  
    def inner():
        c = 30  
        print(b + c)
    return inner #returning inner() function

Note: When returning the inner function, there is no need for round brackets after the function name.

Since we are returning the inner function from the outer function, we need to declare a variable in the main() function to accept and store the function returned from the outer() function as shown below.

#main() function
result = outer() 
print(result)
print(type(result))

So, when we execute the below code, we get the output as,

def outer():
    b = 20  
    def inner():
        c = 30  
        print(b + c)
    return inner #returning inner() function

#main() function
result = outer() 
print(result)
print(type(result))
Output:
.inner at 0x7fadf75000d0>
<class 'function'>

Here, the inner function returned from the outer function gets stored in the variable ‘result’. So, the variable ‘result’ has modified to a function and the same is evident in the result. Now, ‘result’ acts like a function, using which the inner function defined inside the outer function is called as shown below.

def outer():
    b = 20  
    def inner():
        c = 30  
        print(b + c)
    return inner #returning inner() function

#main() function
result = outer() 
result()
Output:
50

It is assumed that a function completes its execution whenever it returns 0 or any values. Here in the above example, the outer() function returns the inner function. So, it has completed its execution. Once the outer function has completed its execution, we are calling the inner function from the main() function and still the inner() function is able to access the variable initialized inside the outer function. How is this happening?

Well!! When the Python interpreter detects the dependency of the inner function on the outer function, it stores the variables declared/initialized inside the outer function even if the outer function completes its execution (deleted from the memory). 

A function that has this property of accessing a variable declared/initialized inside the outer function even after the outer function has deleted from the memory is called a Closure.

When & Why do we need Python Closures?

Python Closures are used when there is a need for data hiding. Also, they are used to bind the data to a function. For instance, in the above code, we have returned the inner function from the outer function and stored (bind) it to the variable ‘result’ to call the inner function further. Similar to this we can bind any data to a function and use that function wherever required. 

Summary

To conclude, a function should satisfy the following three criteria to become a closure. They are,

  • The function should be a nested function
  • The variable inside the inner function must refer to the variable declared/initialized inside the outer function
  • The outer function must return the inner function