🎒 Python Closures: The Magical Backpack of Functions
🚀 Welcome to the World of Closures
Alright, young coder! You’ve made it to one of the coolest and most brain-tingling parts of Python functions — Closures.
Let’s explore closures like we’re in a secret coding workshop, building little magic tools that remember things 🧠💼.
🧠 What Are Closures? (Think: Backpack Trick)
Imagine you’re packing a backpack 🎒 with snacks 🍫 and a notebook 📓. Then you zip it up and give it to a friend. Even though you’re gone, your friend can still unzip it and use what’s inside.
That’s exactly what a closure is in Python.
📌 In our code analogy:
- The backpack = a function defined inside another function.
- The snacks = variables from the outer (enclosing) function.
- The friend using it later = Python remembering those variables even after the outer function is done.
💻 Python Closure Example
Let’s see this concept in action:
def outer():
count = 0
def inner():
nonlocal count
count += 1
return count
return inner
🛠 Line-by-Line Breakdown:
outer()
is our “backpack-packing” function.- Inside it, we define
count = 0
. - Then we define
inner()
, a helper function that adds 1 tocount
. - Instead of calling
inner()
, we return it — the backpack is zipped up and handed off.
Now try this:
counter = outer()
print(counter()) # Output: 1
print(counter()) # Output: 2
print(counter()) # Output: 3
Even though outer()
has finished running, the inner()
function remembers count
. That’s the magic of closures.
🤖 Closures Are Functions with Memory
A closure is a function that:
- is defined inside another function,
- uses variables from the outer function,
- and remembers those variables even after the outer function is done.
This allows you to create dynamic, customized functions with persistent state.
🎮 Real-Life Use Case: A Game Score Tracker
Suppose you’re building a simple game and want to track a player’s score without using global variables. Closures are perfect for this.
def create_score_tracker():
score = 0
def add_points(points):
nonlocal score
score += points
return score
return add_points
Use it like this:
player1 = create_score_tracker()
print(player1(10)) # Output: 10
print(player1(5)) # Output: 15
player2 = create_score_tracker()
print(player2(7)) # Output: 7
Each player gets their own closure, with their own score
stored safely inside. Beautiful, right?
🛑 But Wait — What if nonlocal
Doesn’t Work?
You may have tried this in an online platform like Trinket and gotten an error like:
SyntaxError: no binding for nonlocal 'count' found
That’s because some environments:
- Don’t support
nonlocal
well, - Or you’ve accidentally referenced a variable that’s not in the immediate outer scope.
✅ The Fix: Use a Mutable Object (like a list)
def outer():
count = [0]
def inner():
count[0] += 1
return count[0]
return inner
Now:
counter = outer()
print(counter()) # 1
print(counter()) # 2
Here, we store count
in a list because lists are mutable — meaning we can update their contents without needing nonlocal
.
🧃 Side Note: Reading vs Updating
def outer():
name = "Ekene"
def inner():
return "Hello, " + name # ✅ This is fine — no changes to `name`
return inner
But if you try to change name
:
def outer():
name = "Ekene"
def inner():
name += " the Coder" # ❌ Will throw an error
return name
return inner
Python thinks you’re creating a new local variable, and you get:
UnboundLocalError: local variable 'name' referenced before assignment
📌 Why nonlocal
Is Important in Closures
✅ What nonlocal
does:
- It allows the inner function to modify variables from the outer function.
- Without it, you can’t update outer variables — only read them.
Without nonlocal
:
def outer():
count = 0
def inner():
count += 1 # ❌ This fails without `nonlocal`
return count
return inner
With nonlocal
:
def outer():
count = 0
def inner():
nonlocal count
count += 1
return count
return inner
📝 Practice Questions
-
In your own words, what is a closure?
-
True or False: A closure can remember variables from the function it was created inside.
-
What does this code print?
def outer(): name = "Ekene" def greet(): return "Hello, " + name return greet my_greet = outer() print(my_greet())
-
Write a function called
create_timer()
that returns a function which adds 1 second each time it’s called — and works withoutnonlocal
. -
Why is
nonlocal
important in closures? What happens if you leave it out?