Closure In Python

Transformative Tech Leader | Serial Entrepreneur & Machine Learning Engineer Leveraging 3+ years of expertise in Machine Learning and a background in Web Development, I drive innovation through building, mentoring, and educating. Passionate about harnessing AI to solve real-world problems."
By the end of this lesson, you’ll:
Know what a closure is,
Understand how closures work internally,
Learn why we use closures,
See practical real-world examples,
And get practice exercises to solidify your understanding.
1. What is a Closure in Python?
A closure is a function that remembers variables from its enclosing scope, even if that scope is no longer active.
In other words:
A closure allows a function to access variables from outside its immediate scope, even after the outer function has finished executing.
Example (without closure)
def outer_function():
x = 10
def inner_function():
print(x)
inner_function()
outer_function()
Output:
10
Here, inner_function() accesses x from the outer scope — that’s normal.
But now watch what happens if we return the inner function.
2. How Closures Are Created
Step-by-step Example:
def outer_function():
x = 10 # <- This variable is in the enclosing scope
def inner_function():
print(x) # <- inner_function refers to x
return inner_function # <- returning the function, not calling it
my_func = outer_function() # outer_function() executes and returns inner_function
my_func() # we call the returned function
Output:
10
What happened internally?
When
outer_function()ran, it createdx = 10and definedinner_function.Then it returned
inner_function.Normally, when a function finishes, its local variables (
x) are destroyed.But because
inner_functionremembersx, it stays alive inside the returned function.
That “remembering” is called a closure.
3. Checking if a function is a closure
You can inspect the closure using the __closure__ attribute:
def outer_function():
x = 5
def inner_function():
print(x)
return inner_function
f = outer_function()
print(f.__closure__) # <--- contains the remembered variables
Output:
(<cell at 0x...: int object at 0x...>,)
You can even look inside the closure cell:
print(f.__closure__[0].cell_contents)
Output:
5
That’s how Python stores the enclosed variable.
4. Why Use Closures?
Closures are very powerful and used frequently in Python to:
Preserve state without using global variables or classes.
Encapsulate logic neatly.
Build function factories (functions that return customized functions).
Implement decorators (a key part of advanced Python).
Example 1: Function Factory
You can use closures to create customized functions.
def multiplier(factor):
def multiply(number):
return number * factor
return multiply
double = multiplier(2)
triple = multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15
Here:
Each returned function (
double,triple) “remembers” its ownfactor.factorstays alive even aftermultiplier()finishes.
Example 2: Simple Counter
Closures can maintain state between function calls.
def counter():
count = 0
def increment():
nonlocal count # allows modification of outer variable
count += 1
return count
return increment
count1 = counter()
print(count1()) # 1
print(count1()) # 2
print(count1()) # 3
Without nonlocal, Python would treat count as a new local variable inside increment().
Example 3: Configuration Wrapper
Suppose you want to wrap behavior around a specific configuration.
def greet_config(lang):
def greet(name):
if lang == "en":
print(f"Hello, {name}!")
elif lang == "es":
print(f"Hola, {name}!")
else:
print(f"Hi, {name}!")
return greet
english_greet = greet_config("en")
spanish_greet = greet_config("es")
english_greet("Alice")
spanish_greet("Carlos")
Output:
Hello, Alice!
Hola, Carlos!
Each function remembers its lang configuration.
5. Important Keyword: nonlocal
When using closures, the inner function cannot modify variables from the enclosing scope unless you declare them as nonlocal.
Example:
def outer():
count = 0
def inner():
nonlocal count
count += 1
return count
return inner
f = outer()
print(f()) # 1
print(f()) # 2
Without nonlocal, you’d get an error:
UnboundLocalError: local variable 'count' referenced before assignment
7. Real-World Use Cases of Closures
Decorators — Python decorators are implemented using closures.
def logger(func): def wrapper(*args, **kwargs): print(f"Running {func.__name__}...") return func(*args, **kwargs) return wrapper @logger def greet(name): print(f"Hello, {name}") greet("John")Data hiding — Closures let you encapsulate variables.
Callbacks — Used in event handling (e.g., in GUIs or async code).
Custom Function Generators — Like
multiplier()above.
8. Practice Exercises
Exercise 1: Function Factory
Create a closure power_of(n) that returns a function which raises any number to the power n.
Expected:
square = power_of(2)
cube = power_of(3)
print(square(4)) # 16
print(cube(2)) # 8
Exercise 2: Simple Banking Example
Create a closure bank_account() that:
Starts with a balance of 0.
Has an inner function
transaction(amount)that can deposit or withdraw.Uses
nonlocalto modify the balance.
Expected:
account = bank_account()
print(account(100)) # Deposited 100 → balance = 100
print(account(-50)) # Withdrawn 50 → balance = 50
9. Summary
| Concept | Meaning |
| Closure | A function that remembers variables from its enclosing scope |
| How it’s formed | When a nested function references variables from its enclosing function and is returned |
| Use cases | Function factories, decorators, maintaining state, data hiding |
| Keyword | nonlocal allows modification of enclosed variables |
Key Takeaways
Closures make functions stateful without using classes.
They are foundational for decorators and many advanced Python features.
They provide encapsulation and flexibility in functional programming styles.





