Python Closures
Table of Contents
In the last lesson, we learned about Python Decorators. Now let us learn how closures remember variables from the scope where they were created.
What Is a Closure in Python?
A closure is a nested function that captures and remembers variables from its enclosing function. Even after the outer function has finished, the inner function can still access those variables.
Think of it like a backpack. The inner function carries a backpack of variables from its home. Even when it travels elsewhere, it still has them.
Why Do We Need Closures in Python?
Closures let us create functions with some pre-set behavior. They are useful for creating function factories, decorators, and callbacks.
They also help us avoid global variables and keep our code organized.
How to Create a Closure in Python
A closure is created when a nested function references a variable from its outer function.
def outer(message): def inner(): print(message) return inner
greet = outer("Hello")greet()Output
HelloHere, inner is a closure. It remembers the message variable from outer. Even after outer has finished, inner still has access to message.
How Closures Remember Variables in Python
When we call outer("Hello"), it returns inner. The variable message is captured in the closure. It is not destroyed when outer returns.
def counter(): count = 0 def increment(): nonlocal count count += 1 return count return increment
c = counter()print(c())print(c())print(c())Output
123Each call to c() increments the same count variable. The closure remembers it.
Notice the nonlocal keyword. It tells Python that count is not a local variable of increment. It belongs to the enclosing scope.
A Real-Life Example – Multiplier Factory
Let us create a function that returns a multiplier.
def multiplier(factor): def multiply(number): return number * factor return multiply
double = multiplier(2)triple = multiplier(3)
print(double(5))print(triple(5))Output
1015double remembers factor=2. triple remembers factor=3. They are separate closures.
Another Example – Logger
def logger(prefix): def log(message): print(f"[{prefix}] {message}") return log
info = logger("INFO")error = logger("ERROR")
info("Program started")error("Something went wrong")Output
[INFO] Program started[ERROR] Something went wrongThat is a clean way to create logger functions.
Common Mistakes with Closures in Python
Mistake 1: Forgetting nonlocal for mutable variables
def counter(): count = 0 def increment(): count += 1 return count return incrementThis gives an error. Because count is treated as a local variable.
Fix: Use nonlocal.
nonlocal countMistake 2: Creating closures in a loop
functions = []for i in range(3): def func(): return i functions.append(func)
for f in functions: print(f())Output
222All closures remember the same i, which ends at 2.
Fix: Use a default argument to capture the current value.
for i in range(3): def func(i=i): return i functions.append(func)Now the output is 0 1 2.
Mistake 3: Thinking the outer function runs every time
The outer function runs only once. The inner function remembers the variables from that single run.
Next, we will learn about Python Custom Context Managers and control setup and cleanup with the with statement.