Closures in Python

Parag DurafeParag Durafe
5 min read

Closures are an important concept in programming that allows developers to create functions with a persistent state. In Python, closures are created when a nested function references a variable from its enclosing function. In this article, we will explore the concept of closures in Python and provide a comprehensive guide on how to use them effectively.

What are closures in Python?

In Python, a closure is a function object that has access to variables in its enclosing lexical scope, even after the enclosing function has completed execution. This means that the function can "remember" the values of its enclosing scope, and use them later when it is called.

Closures are created when a nested function references a variable from its enclosing function. Here is an example of a closure in Python:

def outer_function(x):
    def inner_function(y):
        return x + y
    return inner_function
closure = outer_function(10)
result = closure(5)
print(result)
# Output = 15

In this example, the outer_function returns the inner_function, which references the variable x from its enclosing scope. The outer_function is called with the argument 10, which creates a closure with the value of x set to 10. The closure is then assigned to the variable closure.

When the closure is called with the argument 5, it adds 5 to the value of x (which is 10), and returns the result 15.

How to create closures in Python

To create a closure in Python, you need to define a function that references a variable from its enclosing scope, and then return that function. Here is an example of a simple closure that adds a constant value to its input:

def add_constant(constant):
    def adder(x):
        return x + constant
    return adder

In this example, the add_constant function returns the adder function, which references the variable constant from its enclosing scope. The adder function takes an input x and adds the value of constant to it.

To use the closure, you first call the add_constant function with the value of constant you want to use:

closure = add_constant(10)
result = closure(5)
print(result)  
# Output = 15

Now, every time you call the closure with a new input, it will add 10 to that input and return the result.

How to modify variables in closures

In some cases, you may want to modify the value of a variable in a closure. To do this, you need to use the nonlocal keyword to indicate that the variable is not local to the current function, but is instead a variable from the enclosing scope.

Here is an example of a closure that counts the number of times it has been called:

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

In this example, the counter function returns the increment function, which references the variable count from its enclosing scope. The increment function uses the nonlocal keyword to indicate that count is not a local variable, but is instead a variable from the enclosing scope.

Every time the increment function is called, it increments the value of count and returns the new value. To use the closure, you first call the counter function to create a new closure:

closure = counter()
result = closure()  # Output = 1
result = closure()  # Output = 2
result = closure()  # Output = 3

Now, every time you call the closure, it will increment the value of count and return the new value.

How to use closures for memoization

Memoization is a technique for optimizing the performance of functions by caching the results of expensive computations. Closures can be used to implement memoization in Python by creating a closure that stores the results of previous function calls.

Here is an example of a closure that implements memoization for the Fibonacci sequence:

def fibonacci():
    cache = {0: 0, 1: 1}
    def fib(n):
        if n in cache:
            return cache[n]
        result = fib(n-1) + fib(n-2)
        cache[n] = result
        return result
    return fib

In this example, the Fibonacci function returns the fib function, which implements the Fibonacci sequence using memoization. The cache variable is a dictionary that stores the results of previous function calls, and is initialized with the base cases of the sequence (0 and 1).

The fib function checks if the result for n is already in the cache, and returns it if it is. Otherwise, it computes the result recursively using the previous two results and stores the result in the cache before returning it.

To use the closure, you first call the Fibonacci function to create a new closure:

closure = fibonacci()
result = closure(5)  # Output = 15
result = closure(7)  # Output = 28
result = closure(10)  # Output = 55

Now, every time you call the closure with a new input, it will return the result from the cache if it is already available, or compute it and store it in the cache if it is not.

Conclusion

Closures are a powerful tool in Python that allow developers to create functions with persistent state. By referencing variables from their enclosing scope, closures can "remember" the values of those variables and use them later when they are called.

In this article, we have provided a comprehensive guide on how to create closures in Python, modify variables in closures, and use closures for memoization. We hope that this article has helped you understand the power of closures in Python and how to use them effectively in your programs.

0
Subscribe to my newsletter

Read articles from Parag Durafe directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Parag Durafe
Parag Durafe