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?
adder(5)returnswrapperwherex=5result(6)computes5 + 6 = 11result(11)computes5 + 11 = 16
The final output is 16.