A Practical Guide to Python Closures With Examples

Have you ever thought about how Python functions can retain values from their surroundings even after finishing the running? This is made possible with Python Closures. Closures are like chunks of code or functionality that remember the context or scope in which they were created or specified.

Our Today’s guide is all about Python Closures, their working, the method to create closures, access and modify enclosed variables, return closures, and create nested closures. Moreover, we will also compare closures and classes based on different aspects. So, let’s get started!

What are Closures in Python

Python Closures are the types of functions that capture and remember the values of the variables in the respective scope, even after the scope has finished executing. They permit the functions to retain access to variables from the containing functions.

In short, we can say that closures create a sort of snapshot of the environment in which they were created.

How to Create Closures in Python

Closures can be defined when a nested function refers to the variables from its corresponding function. The nested function then becomes a closure when it is returned from the containing function.

This enables the closure to maintain the relevant access to those enclosed variables.

In this example, we have a closure named “inner_function()” that will capture the value of the “x” variable from its containing scope, which is “outer_function()“.

Then, the “closure_instance” retains the enclosed values of the “x” variable when invoked later.

def outer_function(x):
    def inner_function(y):
        return x + y
    return inner_function

closure_instance = outer_function(10)
result = closure_instance(5)
print(result)
Create Closures in Python
Create Closures in Python

How to Access Enclosed Variables in Python

Remember that closure can access and use variables from their containing function, even after the respective function completed its execution.

Here, the “increment()” closure has access to the enclosed variable named “count” from the “counter()” function. Additionally, the closure will retain access to “count” when invoked multiple times.

def counter():
    count = 0
   
    def increment():
        nonlocal count
        count += 1
        return count
   
    return increment

counter_instance = counter()
result = counter_instance()
print(result)
Access Enclosed Variables in Python
Access Enclosed Variables in Python

Modify Enclosed Variables in Python

The “nonlocal” keyword can be utilized for modifying the values of the enclosed variable. This enables the closure to update or change the values of the variables in their respective function’s scope.

Here, in the provided code, the “multiply()” closure multiplies the values of the defined variable “x” by the “factor” which is enclosed from the “multiplier ()” function.

This closure can modify the value of the “factor” variable because of the “nonlocal” keyword.

def multiplier(factor):
    def multiply(x):
        return x * factor
    return multiply

double = multiplier(2)
result = double(5)
print(result)
Modify Enclosed Variables in Python
Modify Enclosed Variables in Python

Return Closure in Python

Returning closure from Python functions permits the dynamic creation of functions with different enclosed values. This is primarily helpful in use cases where it is required to generate customized functions on the fly.

In the given code, the “power_function()” returns or outputs a “power()” closure that evaluates the power with the defined “exponent“.

So, here, by creating “square()” and “cube()” closures with different exponents, we are generating a function for calculating the squares and cubes of the passed argument.

def power_function(exponent):
    def power(x):
        return x ** exponent
    return power

square = power_function(2)
cube = power_function(3)

result1 = square(4)
print('square:', result1)
result2 = cube(3)  
print('cube:', result2)
Return Closure in Python
Return Closure in Python

Nested Closures in Python

In Python, you can also create nested closures. This process allows you to create a hierarchy of encapsulated functions. Additionally, nested closures can lead to more complex and organized code structures.

According to the provided code, we have three levels of nested closures:

  • inner_function()” inside the “middle_function()“.
  • middle_function()” inside the “outer_function()“.

Note that the “closure_instance()” has been invoked in a chained manner, ultimately displaying the sum of all three values.

def outer_function(x):
    def middle_function(y):
        def inner_function(z):
            return x + y + z
        return inner_function
    return middle_function

closure_instance = outer_function(10)
result = closure_instance(5)(3)
print(result)
Nested Closures in Python
Nested Closures in Python

Closures vs Classes

Let’s check how closures are different from Python classes in terms of the listed aspects:

Aspect Python Closures Python Classes
Encapsulation Encapsulates data and behavior within a scope. Encapsulates data and behavior using methods.
State Management Maintains the internal state within the closure. Maintains state using instance variables.
Lightweight Typically more lightweight and concise. Offers a structured approach with class methods.
Hierarchy Easily nested to create complex structures. Supports inheritance for creating hierarchies.
Functional Paradigm Essential for functional programming concepts. One of many tools for implementing OOP.
Flexibility Well-suited for higher-order functions. Suited for creating objects with methods.
Extensibility Limited extensibility, designed for simplicity. Highly extensible with inheritance and polymorphism.
Use Case Functional-style programming, closures with dynamic behavior. Object-oriented programming, creating structured and hierarchical classes.

Closures in Functional Programming

Closures play a significant role in functional programming paradigms. They permit functions to be passed as arguments, returned from other functions, or assigned to variables. This ultimately enables the high-order functions and makes the code more concise.

In the following program, the “apply_operation()” function accepts a closure named “operation” and two operands, “x” and “y“.

They permit performing different types of operations such as addition and multiplication, with the same function structure.

def apply_operation(operation, x, y):
    return operation(x, y)

def add(x, y):
    return x + y

def multiply(x, y):
    return x * y

result1 = apply_operation(add, 3, 5)  
print('Addition:', result1)  
result2 = apply_operation(multiply, 2, 4)  
print('Multiplication:', result2)
Closures in Functional Programming
Closures in Functional Programming

That’s all from this effective guide regarding Python closures.

Conclusion

Closures might sound like a complex concept, but they are the most handy and robust constructs in Python. With the help of closures, you can create functions that can encapsulate both data and the code.

They also provide a function to the Python functions for retaining access to the variables. Additionally, closures enable you to create efficient and flexible code.

Want to explore and learn more related to Python, do check out our dedicated Python Tutorial Series!

If you read this far, tweet to the author to show them you care. Tweet a thanks
As a professional content writer with 3 years of experience, I specialize in creating high-quality, SEO-optimized content that engages, attracts, and retains the audience.

Each tutorial at GeeksVeda is created by a team of experienced writers so that it meets our high-quality standards.

Join the GeeksVeda Weekly Newsletter (More Than 5,467 Programmers Have Subscribed)
Was this article helpful? Please add a comment to show your appreciation and support.

Got Something to Say? Join the Discussion...