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
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
Explanation
- The
count
variable is initialized to0
within themake_counter
function. - Every time we call the
increment
function (which is returned bymake_counter
), it modifies thecount
variable in the enclosing scope using thenonlocal
keyword. Thanks to the closure property, theincrement
function retains access tocount
even after themake_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)