Python Closures: A Practical Guide with Examples Read it later

5/5 - (2 votes)

Python is an object-oriented programming language that supports functional programming. In functional programming, functions are treated as first-class objects. This means that functions can be assigned to variables, passed as arguments to other functions, and returned as values from functions. One of the powerful concepts in functional programming is the use of closures. In this blog post, we will explore what closures are, how they work, and how to use them in Python programming.

Before learning about closures in python make sure you have a good knowledge of Python Inner Function or Nested Function, as those examples will be extended in this topic.

Let’s learn about Closures in Python.

What are Closures in Python?

A closure is a function object that has access to variables in its enclosing lexical scope, even when the function is called outside that scope. A closure can “remember” the values of the variables in the enclosing scope, and use them each time it is called. The concept of closures is based on the concept of lexical scoping.

python closures
Image Source – towardsdatascience.com

To understand closures, let’s look at an example. Consider the following code snippet:

def outer_func(x):
    def inner_func(y):
        return x + y
    return inner_func

closure_func = outer_func(10)
print(closure_func(5)) # Output: 15

In the above example, we have defined a function outer_func that takes an argument x. Inside outer_func, we have defined another function inner_func that takes an argument y and returns the sum of x and y. Finally, outer_func returns inner_func. We then call outer_func with an argument of 10 and assign the result to closure_func. We can then call closure_func with an argument of 5 and get the output 15.

In this example, inner_func is a closure because it has access to the variable x in the enclosing scope of outer_func, even though outer_func has returned and is no longer executing. Each time we call closure_func, it “remembers” the value of x that was passed to outer_func.

How Closures in Python Work?

In Python, when a function is defined inside another function, it creates a closure. The inner function has access to the variables in the outer function’s scope.

When the outer function returns, the inner function is still able to access those variables because they are “closed over” by the closure.

In our example above, when outer_func is called with an argument of 10, it creates a closure that “closes over” the variable x with a value of 10. When inner_func is called with an argument of 5, it adds 5 to the value of x and returns the result.

Benefits of Closures

Closures have several benefits in Python programming. They can be used to:

  1. Create function factories: A function factory is a function that returns another function. Closures can be used to create function factories because they allow you to “remember” the state of the factory function.
  2. Implement data hiding: Closures can be used to hide data within a function. This is useful when you want to create a private variable that cannot be accessed from outside the function.
  3. Create decorators: Decorators are a powerful feature in Python that allow you to modify the behavior of functions or classes. Closures can be used to create decorators because they allow you to wrap a function with additional functionality.

Let’s learn the use case for closures in detail.

Function Factory Using Python Closures

A function factory is a function that returns another function. This can be useful when you need to create multiple functions with similar functionality.

Let’s take a look at an example of a function factory:

def multiply_by(x):
    def multiplier(n):
        return n * x
    return multiplier

double = multiply_by(2)
triple = multiply_by(3)

print(double(5)) # Output: 10
print(triple(5)) # Output: 15

In this example, we define a function called multiply_by that takes an argument x. multiply_by returns an inner function called multiplier that takes an argument n and returns the product of n and x.

Next, we call multiply_by with the argument 2, which returns a function that doubles its input. We assign this function to the variable double.

We then call double with the argument 5, which returns 10.

We repeat the process for triple, which is a function that triples its input. We call triple with the argument 5, which returns 15.

Decorators Using Closures in Python

Closures are often used to create decorators, which are functions that modify the behavior of other functions. Decorators are a powerful tool in Python, and they can be used to add functionality to existing functions without modifying their code. Here’s an example:

def my_decorator(func):
    def wrapper():
        print('Something is happening before the function is called.')
        func()
        print('Something is happening after the function is called.')

    return wrapper

@my_decorator
def say_hello():
    print('Hello!')

say_hello()

In this example, we define a decorator called my_decorator. This decorator takes a function as an argument and returns a new function that wraps the original function.

When we apply the @my_decorator decorator to the say_hello function, it will modify the behavior of the function by adding a few print statements.

Event Handlers Using Python Closures

Event handlers are functions that are executed in response to an event, such as a button click or a key press.

Let’s take a look at an example of an event handler:

def click_handler(button):
    def handler():
        print(f"{button} clicked!")
    return handler

button1 = click_handler("Button 1")
button2 = click_handler("Button 2")

button1() # Output: "Button 1 clicked!"
button2() # Output: "Button 2 clicked!"

In this example, we define a function called click_handler that takes an argument button. click_handler returns an inner function called handler that prints a message to the console when called.

Next, we call click_handler with the argument "Button 1", which returns a function that prints "Button 1 clicked!" when called. We assign this function to the variable button1.

We then call button1, which executes the handler function and prints "Button 1 clicked!" to the console.

We repeat the process for button2, which is a function that prints "Button 2 clicked!" when called.

Create private variables Using Python Closures

Closures can also be used to create private variables in Python. A private variable is a variable that cannot be accessed or modified from outside the function. Here’s an example:

def outer_function():
    count = 0

    def inner_function():
        nonlocal count
        count += 1
        print(count)

    return inner_function

my_func = outer_function()
my_func()
my_func()

In this example, we define an outer function that returns an inner function. The count variable is a private variable that can only be accessed and modified by the inner function. When we call the my_func function twice, it will print 1 and 2.

Memoization Using Closures

Memoization is a technique for caching the results of expensive function calls and returning the cached result when the same inputs occur again. This can improve performance by avoiding unnecessary computation.

Let’s take a look at an example of memoization:

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

fib = fibonacci()

print(fib(10)) # Output: 55

In this example, we define a function called fibonacci that returns an inner function called fib. fib implements the Fibonacci sequence recursively.

We also define a cache dictionary to store the results of previous calls to fib.

When fib is called with an argument n, it first checks if the result for n is in the cache. If it is, it returns the cached result. Otherwise, it computes the result and stores it in the cache before returning it.

Finally, we call fibonacci to get the fib function and assign it to the variable fib. We then call fib with the argument 10, which returns 55.

Criteria for Closures

To work with closures in Python, there are three key criteria that must be met:

  1. The presence of nested functions.
  2. The use of a variable from the outer function in the inner function.
  3. The outer function must return the inner function.

When these conditions are satisfied, Python utilizes its closure feature. All of the examples provided in this blog fulfill these criteria and demonstrate how Python closures work in practice.

Hope you like it.

Learn more about Python like decorator, generator, inner function, and store web scraping to CSV using Python.

Was This Article Helpful?

Leave a Reply

Your email address will not be published. Required fields are marked *