Understanding Python Closures: How They Work and When to Use Them

What Is a Closure in Python?

In Python, a closure occurs when a nested function references variables from its enclosing scope. The outer function is called the enclosing function, while the inner function is referred to as the nested function.

A closure forms when three conditions are met:

  • An inner function is defined within an outer function
  • The inner function uses variables from the outer function's scope
  • The outer function returns the inner function as its result

Typically, when a function completes execution, all its local variables are garbage collected and freed from memory. However, closures represent a special case. When the outer function finishes execution, it detects that its local variables will be used by the nested function. Instead of deallocating these variables, the outer function binds them to the inner function before terminating.

Closure Implementation Example

Consider the following code demonstrating a basic closure:

if name == 'main': # Calling outer function with argument 5 # Outer function creates inner function and returns its reference func_instance = outer(5) # The outer function's local variables (a=5, b=20) are now bound to inner func_instance() # Output: Sum: 25

another_func = outer(10)
another_func()  # Output: Sum: 30

</div>Understanding Function References
---------------------------------

To grasp closures fully, you must understand that in Python, everything is an object—including functions. When you write `def my_func(): pass`, Python allocates memory space containing the function's code and local variables. The variable name `my_func` stores a reference (essentially a memory address) to this function object.

You can assign the same function reference to multiple variables:

<div>```
x = outer(5)
y = outer(5)

Variible Binding Mechanism

When the outer function executes, it detects that the inner function references its local variables. Rather than discarding these variables upon completion, the outer function binds them to the inner function. This binding persists even after the outer function returns.

Each invocation of the outer function creates a new inner function instance with its own captured variables:


</div>Modifying Closure Variables
---------------------------

Closures can read captured variables freely, but modifying them requires special handling. Python provides two approaches:

### Method 1: Using the nonlocal Keyword

The `nonlocal` keyword (Python 3) tells Python to search the enclosing scope for the variable rather than treating it as a local variable:

<div>```
def counter(start):
    count = start
    
    def increment():
        nonlocal count
        count += 1
        print(f"Current count: {count}")
    
    return increment

if __name__ == '__main__':
    counter_func = counter(0)
    counter_func()  # Current count: 1
    counter_func()  # Current count: 2
    counter_func()  # Current count: 3

In Python 2 (which lacks nonlocal) or when you prefer this approach, wrap variables in a mutable container like a list:

def increment():
    count[0] += 1
    print(f"Current count: {count[0]}")

return increment

if name == 'main': counter_func = counter(0) counter_func() # Current count: 1 counter_func() # Current count: 2


</div>Shared Closure State
--------------------

All invocations of the same closure function share the same captured variables. The closure variables persist across multiple function calls:

<div>```
def accumulator(initial):
    total = initial
    
    def add(value):
        nonlocal total
        total += value
        return total
    
    return add

if __name__ == '__main__':
    acc = accumulator(50)
    print(acc(10))   # 60
    print(acc(25))   # 85
    print(acc(5))    # 90

Practical Applications of Closures

1. Decorators

Decorators are one of the most common closure applications. They wrap existing functions to add functionality before or after the original function executes:

def timing_decorator(func): def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(f"{func.name} executed in {end - start:.4f} seconds") return result return wrapper

@timing_decorator def slow_operation(): time.sleep(1) print("Operation completed")

slow_operation()


</div>### 2. Object-Oriented Programming Alternative

Closures can implement encapsulation and state retention, similar to how objects work in OOP. While Python typically uses classes for this purpose, closures provide an alternative functional approach:

<div>```
def create_person(name, age):
    def get_info():
        return f"Name: {name}, Age: {age}"
    
    def birthday():
        nonlocal age
        age += 1
        return f"Happy birthday! Now {age} years old"
    
    return {'get_info': get_info, 'birthday': birthday}

person = create_person("Alice", 30)
print(person['get_info']())
print(person['birthday']())

Closures can implement the singleton pattern, ensuring only one instance of a class exists:

def get_instance(*args, **kwargs):
    nonlocal instance
    if instance is None:
        instance = cls(*args, **kwargs)
    return instance

return get_instance

@singleton class Database: def init(self): print("Database connection established")

db1 = Database() db2 = Database() print(db1 is db2) # True - same instance


</div>Closure Example Explained
-------------------------

Consider this nested function example:

<div>```
def adder(x):
    def wrapper(y):
        return x + y
    return wrapper

result = adder(5)
print(result(result(6)))  # What is the output?

  1. adder(5) returns wrapper where x=5
  2. result(6) computes 5 + 6 = 11
  3. result(11) computes 5 + 11 = 16

The final output is 16.

Tags: python closures nested-functions nonlocal decorators

Posted on Thu, 14 May 2026 21:56:20 +0000 by greepit