DEV Community

Mehedi Bappi
Mehedi Bappi

Posted on

Understanding Closures in Python

In Python, closures are an important concept that allows a function to "remember" the environment in which it was created, even after the function has finished executing. Closures allow us to implement stateful functions without using global variables or class instances.

In this post, we'll explore closures by implementing a simple counter using the nonlocal keyword. Let's dive into it!

What is a Closure?

A closure occurs when a nested function refers to a variable from its enclosing scope, allowing it to retain access to those variables even after the enclosing function has finished executing. Closures are particularly useful when you want to encapsulate a state or behaviour within a function.

The nonlocal Keyword

In Python, we use the nonlocal keyword to modify a variable in the nearest enclosing scope that is not global. Without the nonlocal keyword, an inner function cannot modify variables in its enclosing scope; it would instead create a new local variable. The nonlocal keyword resolves this by telling Python that we want to work with a variable from the enclosing scope.

Implementing a Counter with Closures

Let's create a simple counter function that uses closures to keep track of the count without relying on global variables or a class.

Step 1: Define the make_counter Function

We’ll create a function called make_counter, which will return an inner function increment. The inner function will increase a count variable each time it is called.

Step 2: Use nonlocal to Modify the Count Variable

To ensure the increment function modifies the count variable defined in the make_counter function's scope, we'll use the nonlocal keyword.

Here’s the implementation:

def make_counter():
    count = 0  # Variable in the enclosing scope

    def increment():
        nonlocal count  # Tell Python to modify the `count` from the enclosing scope
        count += 1  # Increment the counter
        return count  # Return the current count

    return increment  # Return the inner function, which forms the closure
Enter fullscreen mode Exit fullscreen mode

Step 3: Using the Counter

Now that we have the make_counter function, we can create an instance of the counter and call it multiple times to see the counter increment.

counter = make_counter()

print(counter())  # Output: 1
print(counter())  # Output: 2
print(counter())  # Output: 3
print(counter())  # Output: 4
print(counter())  # Output: 5
Enter fullscreen mode Exit fullscreen mode

Explanation

  • The count variable is initialized to 0 within the make_counter function.
  • Every time we call the increment function (which is returned by make_counter), it modifies the count variable in the enclosing scope using the nonlocal keyword. Thanks to the closure property, the increment function retains access to count even after the make_counter function finishes execution.

Why Use Closures?

Closures provide a powerful and elegant way to encapsulate state within functions. They are especially useful in scenarios where:

  • You want to keep track of a value without exposing it to the global scope.
  • You need a function to remember its previous state across multiple calls (like our counter-example).
  • You don’t want to use global variables or create classes for simple state management.

Closures can be used for more advanced use cases such as decorators, memoization, and callbacks.

Top comments (0)