Python Closures

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

Hello

Here, 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

1
2
3

Each 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

10
15

double 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 wrong

That 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 increment

This gives an error. Because count is treated as a local variable.

Fix: Use nonlocal.

nonlocal count

Mistake 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

2
2
2

All 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.