🏠 The House of Variables: Understanding Scope and Closures
Imagine your code is a big house with many rooms. Each room has its own secrets, and some secrets can only be found in certain rooms. Let’s explore this magical house together!
🌟 What is Scope?
Scope is like asking: “Where can I find this toy?”
Think of it this way:
- Your bedroom has YOUR toys
- The living room has FAMILY toys everyone can use
- Your toy box inside your room? Only YOU know what’s in there!
In Python, scope means: Where a variable lives and who can see it.
🔤 The LEGB Rule: Four Rooms in Our House
Python looks for variables in 4 places, always in this order:
L → E → G → B
The Four Rooms:
| Letter | Room Name | What It Means |
|---|---|---|
| L | Local | Inside the current function |
| E | Enclosing | Inside outer functions |
| G | Global | At the top level of your file |
| B | Built-in | Python’s own special tools |
🏠 Room 1: LOCAL Scope (Your Bedroom)
Variables created inside a function stay inside that function.
def my_room():
toy = "teddy bear" # LOCAL
print(toy) # Works!
my_room()
print(toy) # ERROR! Can't see it!
Think of it: Your teddy bear is in YOUR room. Mom can’t see it from the kitchen!
🏠 Room 2: ENCLOSING Scope (The Big Room Around You)
When a function is inside another function, the inner one can see the outer one’s stuff.
def living_room():
snack = "cookies" # ENCLOSING
def my_corner():
print(snack) # Can see it!
my_corner()
living_room() # Prints: cookies
Think of it: You’re playing in a corner of the living room. You can see the cookies on the coffee table!
🌍 Room 3: GLOBAL Scope (The Whole House)
Variables at the top of your file (not inside any function) are global.
family_pet = "dog" # GLOBAL
def any_room():
print(family_pet) # Everyone sees it!
any_room() # Prints: dog
Think of it: The family dog walks everywhere in the house. Everyone knows him!
🔧 Room 4: BUILT-IN Scope (Python’s Magic Toolbox)
Python has special words like print, len, sum that work everywhere.
numbers = [1, 2, 3]
print(len(numbers)) # 3
# len is BUILT-IN
Think of it: Like electricity in your house—it’s always there, everywhere!
🔍 How Python Searches (LEGB in Action)
graph TD A[Looking for variable X] --> B{Is X in LOCAL?} B -->|Yes| C[Use it!] B -->|No| D{Is X in ENCLOSING?} D -->|Yes| C D -->|No| E{Is X in GLOBAL?} E -->|Yes| C E -->|No| F{Is X in BUILT-IN?} F -->|Yes| C F -->|No| G[ERROR! Not found]
🌐 The global Keyword: Talking to the Whole House
Problem: What if you want to change a house-wide rule from your room?
score = 0 # GLOBAL
def play_game():
global score # "I want the HOUSE score!"
score = score + 10
play_game()
print(score) # 10 (it changed!)
Without global:
score = 0
def play_game():
score = score + 10 # ERROR!
# Python thinks you want a LOCAL score
# But you're trying to read it first!
play_game()
The Rule: Use global when you need to change a variable that lives outside your function.
⚠️ Important Warning:
- You CAN read global variables without
global - You NEED
globalto change them
🔗 The nonlocal Keyword: Talking to the Room Around You
Problem: What about changing something in the room AROUND your function?
def outer_room():
treats = 5 # ENCLOSING
def inner_room():
nonlocal treats # "The outer room's treats!"
treats = treats - 1
inner_room()
print(treats) # 4
outer_room()
When to Use Which?
| Keyword | Use When… |
|---|---|
global |
Changing a variable at the top of your file |
nonlocal |
Changing a variable in an outer function |
🎁 Closures: Functions That Remember
A closure is like a lunchbox. When you make it in the morning, you put your sandwich inside. Later at school, you open it and your sandwich is still there!
What Makes a Closure?
- A function inside another function
- The inner function uses something from the outer function
- The outer function returns the inner function
def make_lunchbox(food):
# food is "packed" in the lunchbox
def open_lunchbox():
return f"Yum! {food}!"
return open_lunchbox
my_lunch = make_lunchbox("sandwich")
print(my_lunch()) # Yum! sandwich!
The magic: Even though make_lunchbox finished running, my_lunch still remembers “sandwich”!
🧮 Closures for Counting
def make_counter():
count = 0 # This gets "remembered"
def add_one():
nonlocal count
count = count + 1
return count
return add_one
counter = make_counter()
print(counter()) # 1
print(counter()) # 2
print(counter()) # 3
Each call remembers the previous count!
🏭 Closures as Function Factories
def make_multiplier(times):
def multiply(number):
return number * times
return multiply
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15
Think of it: You built two different machines—one doubles, one triples!
🎯 Quick Summary
graph LR A[Scope & Closures] --> B[LEGB Rule] A --> C[Keywords] A --> D[Closures] B --> B1[L: Local] B --> B2[E: Enclosing] B --> B3[G: Global] B --> B4[B: Built-in] C --> C1[global: change top-level vars] C --> C2[nonlocal: change outer func vars] D --> D1[Functions that remember] D --> D2[Inner function + outer data]
💡 Golden Rules to Remember
- Variables prefer to be LOCAL - Python creates local ones first
- Reading is easy - You can read outer variables anytime
- Changing needs permission - Use
globalornonlocal - Closures are memory keepers - They hold onto values forever
- LEGB is the search order - Local → Enclosing → Global → Built-in
🎮 Real-World Example: A Simple Game
player_name = "Hero" # GLOBAL
def create_player():
health = 100 # Will be enclosed
def take_damage(amount):
nonlocal health
health = health - amount
print(f"{player_name} has {health} HP")
def heal(amount):
nonlocal health
health = health + amount
print(f"{player_name} has {health} HP")
return take_damage, heal
hurt, heal = create_player()
hurt(30) # Hero has 70 HP
heal(20) # Hero has 90 HP
See how everything works together?
player_nameis GLOBAL (everyone knows it)healthis ENCLOSING (both functions share it)amountis LOCAL (each call gets its own)
Now you know the secret of the House of Variables! You understand which room each variable lives in, how to change them, and how to create magical closures that remember things forever. 🏆