Python Closures

You already know what nested functions are. Closures can be viewed as the most useful application of nested functions. Before starting with closures, let’s talk a bit about nested functions.

We know that a nested function is a function which is defined inside another function. Let’s look at a simple program having a nested function.

def outer(x):
def inner():
print(x)

inner()

outer("Hey there!")

Hey there!

The function inner() is defined inside the function outer(), and so inner() is a nested function. We called outer() by passing "Hey there!" as the argument. Thus, its parameter x got the value "Hey there!". Since a nested function can access the variable defined in the function in which it is created, we printed the value of x inside the nested function inner().

Look at another example.

def outer(x):
def inner():
print("This is the inner function")
return x

return inner()

print(outer("Hey there!"))

This is the inner function
Hey there!

The statement return inner() inside the outer() function calls the inner() function and returns the value returned by the inner() function. Inside the inner() function, the value of x is returned.

So this was just a recap of nested functions. Let’s now talk about closures.

Closures in Python

In the last example, the outer() function returned the value returned by the nested inner() function. What if you want to return the entire functionality of the inner() function from the outer() function?

Yes, we can do so by replacing return inner() by return inner in the outer() function. return inner() will call the inner() function and then return the value returned by the inner() function, whereas return inner will return the entire inner() function. This is demonstrated in the following example.

def outer(x):
def inner():
print("This is the inner function")
print(x)

return inner  # returning inner function

func = outer("Hey there!")
print(type(func))
func()

<class 'function'>
This is the inner function
Hey there!

When the outer() function is called with the argument "Hey there!", it returns the inner() function instead of calling it. The returned inner() function is assigned to the variable func. Thus, func stores the inner() function and hence printing type(func) displayed <class 'function'>. Therefore, func now has all the functionalities of the inner() function and thus behaves as a function.

On calling the function func(), the string "This is the inner function" and the value of x ("Hey there!") got printed. We got the same output which we would have got on calling inner(). But wait, x was a variable defined in the outer() function but the outer function has already finished executing, then how does func() remember the value of x?

This is exactly what a closure does. A closure is a technique that binds (attaches) some data to the code. When the outer() function returned the inner() function, it attached the value of the variable x with the code of the inner() function and then returned the inner() function. Thus, we have a closure in the above program.

This value attached to the code is remembered even when the function in which it was defined is no longer executing. Therefore, on calling func(), the value of x got printed even when the outer function was not getting executed, because the value of x is attached to the code returned by the outer() function and assigned to func.

Hope you understood the concept of closures. Summing up, a closure is a technique in which a function returns its nested function after attaching some data (value of x in the above example) to the code of the nested function. This returned function is assigned to some variable (func in the above example) which can be used to execute the code of the assigned function whenever called, while also remembering the data attached.

In the above example, let’s see what would happen if we delete the outer() function after returning the inner() function.

def outer(x):
def inner():
print("This is the inner function")
print(x)

return inner  # returning inner function

func = outer("Hey there!")
del outer  # deleting outer function
func()

This is the inner function
Hey there!

After assigning the returned inner() function to the variable func, the outer() function is deleted by writing del outer. Then on calling func(), we are still getting the output. So, even after deleting the original function, the returned function is still giving the output. Or, we can say that func() remembers the context (functionality, variables defined, etc) of the deleted function.

Look at another example.

def outer():
x = "Hey there!"

def inner():
print("This is the inner function")
print(x)

return inner  # returning inner function

func = outer()
func()

This is the inner function
Hey there!

This is similar to the previous examples. Here also, the inner() function is using the variable x defined inside the outside function.

Let’s see another example in which a closure is used.

def outer(x):
def inner(y):
print(x + y)

return inner

func = outer(10)
func(5)

15

Before reading the explanation, try to understand what is happening in this program.

The outer() function is called by passing 10 as argument, making its parameter x equal to 10. It returns its nested function inner() and assigns it to the variable func. So, func stores the inner() function with x equal to 10. Then the func() function is called by passing 5 as argument by writing func(5), thus making its parameter y equal to 5. Inside the body of func(), the sum x + y is printed.

We can also obtain the above result as follows.

def outer(x):
def inner(y):
return x + y

return inner

func = outer(10)
print(func(5))

15

In the next example, we are returning the sum of the elements of a list.

def outer():
sum = 0

def inner(mylist):
nonlocal sum
for num in mylist:
sum = sum + num
return sum

return inner  # returning inner function

func = outer()
mylist = [1, 2, 3, 4, 5, 6]
print(func(mylist))

21

Try to understand the above code yourself. If you have, that means you have understood closures.

So we have seen different examples of closures. But when should we use closures? Let’s find the answer to it in the next section.

Significance of Closures

The first benefit of closures is that we can execute the functionality of the nested function anytime even after its outer function is not executing.

When we have only one or two (preferably one) methods in a class, then also it is better to use closures instead of class.

Assume that we have the following class with a single method print_message() inside it.

class CreateMessage:
def __init__(self, msg):
self.msg = msg

def print_message(self, name):
print("Hi", name)
print(self.msg)

message = CreateMessage("Welcome to the Python course")

print("Printing 1st message----")
message.print_message("John")

print("Printing 2nd message----")
message.print_message("Sam")

Printing 1st message----
Hi John
Welcome to the Python course
Printing 2nd message----
Hi Sam
Welcome to the Python course

We created an object message of the class CreateMessage by passing the value "Welcome to the Python course" to the msg parameter of the constructor. Thus, in the constructor, self.msg = msg made the attribute msg equal to "Welcome to the Python course". Then we used the message object to call the print_message() method with different arguments every time.

Note that we used a class here to store a single method print_message() because we also wanted to store some data (msg) which is used in the method.

So according to you, is it a good decision to create a class for a single method just because we want to store some additional data? No, it isn’t. For such scenarios, we can use closures instead. Let’s see how.

def create_message(msg):
def print_message(name):
print("Hi", name)
print(msg)
return print_message

message = create_message("Welcome to the Python course")

print("Printing 1st message----")
message("John")

print("Printing 2nd message----")
message("Sam")

Printing 1st message----
Hi John
Welcome to the Python course
Printing 2nd message----
Hi Sam
Welcome to the Python course

We defined a function create_message() with a nested function print_message(). When the create_message() function is called, we create a closure by returning the nested function print_message() to a variable message. So, the variable message now stores the functionality of the function print_message() with the value of the variable msg as "Welcome to the Python course". Now whenever the function message() is called, it implements the functionality of print_inner(). We got the same result as in the previous example having class.

Thus, we were able to use closures instead of class when it has just one method.

Look at another example.

class CreateMessage:
def __init__(self, msg):
self.msg = msg

def print_message(self, name):
print("Hi", name)
print(self.msg)

print("Object 1----")
message1 = CreateMessage("Welcome to the Python course")

print("Printing 1st message----")
message1.print_message("John")

print("Printing 2nd message----")
message1.print_message("Sam")

print("Object 2----")
message2 = CreateMessage("Welcome to the C# course")

print("Printing 1st message----")
message2.print_message("Maria")

print("Printing 2nd message----")
message2.print_message("Peter")

Object 1----
Printing 1st message----
Hi John
Welcome to the Python course
Printing 2nd message----
Hi Sam
Welcome to the Python course
Object 2----
Printing 1st message----
Hi Maria
Welcome to the C# course
Printing 2nd message----
Hi Peter
Welcome to the C# course

Suppose the students John and Sam want to learn Python and Maria and Peter want to learn C#. For that, in the above example, we created an object message1 with attribute msg equal to "Welcome to the Python course", and an object message2 with msg equal to "Welcome to the C# course".

This can be implemented using closures as follows.

def create_message(msg):
def print_message(name):
print("Hi", name)
print(msg)
return print_message

print("Variable 1----")
message1 = create_message("Welcome to the Python course")

print("Printing 1st message----")
message1("John")

print("Printing 2nd message----")
message1("Sam")

print("Variable 2----")
message2 = create_message("Welcome to the C# course")

print("Printing 1st message----")
message2("Maria")

print("Printing 2nd message----")
message2("Peter")

Variable 1----
Printing 1st message----
Hi John
Welcome to the Python course
Printing 2nd message----
Hi Sam
Welcome to the Python course
Variable 2----
Printing 1st message----
Hi Maria
Welcome to the C# course
Printing 2nd message----
Hi Peter
Welcome to the C# course

Here the variables message1 and message2 store the code of the print_message() function with the values of msg equal to "Welcome to the Python course" and "Welcome to the C# course" respectively.

If a class has a single method, then using closure instead of class is a better choice.

If the number of methods or the number of attributes in a class increase, then go for class. But for a single method and a few attributes, using closures is a better option because it is faster. However, the choice is yours.

Closures are also used for creating decorators. We will read about decorators in the next chapter.

So this was all about closures. We looked at different examples of closures and also discussed its significance. Make yourself comfortable in closures by practicing more questions on it and feel free to ask your doubts in the discussion section.

To learn from simple videos, you can always look at our Python video course on CodesDope Pro. It has over 500 practice questions and over 20 projects.
When I let go of what I am, I become what I might be.
- Lao Tzu