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)
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)
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)
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)
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)
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)
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!