Scope: Local vs Global (LEGB in 5 Minutes)
Where a variable is created decides where it can be seen. That’s scope — and understanding it explains a whole family of “why is my variable not defined / why did it not change” bugs in one go.
The two levels you need today
total = 0 # GLOBAL — created at file level
def add_points(points):
bonus = 10 # LOCAL — created inside the function
total = total + points # 💥 UnboundLocalError!
- Global variables live at the file’s top level — visible everywhere, including inside functions (readable!)
- Local variables live inside a function — born when the function runs, destroyed when it ends
Reading globals works; writing them doesn’t
site_name = "Tech With Galvan" # global
def show_site():
print(site_name) # ✅ reading a global — works
show_site() # Tech With Galvan
Reading is fine. But assignment creates a NEW local variable:
count = 0 # global
def increase():
count = 100 # ⚠️ this CREATES A LOCAL count — global untouched!
increase()
print(count) # 0 — the global never changed
Python’s rule: assign to a name inside a function, and Python treats it as local — unless you explicitly say otherwise. That’s why total = total + points crashed earlier: Python sees the assignment, decides total is local, then discovers you’re reading it before the local exists.
The fix #1: don’t reassign — use return (the professional way)
count = 0
def increase(count):
return count + 1 # compute on the input, return the result
count = increase(count) # caller updates the global
print(count) # 1
Pass data in, return data out. No global mutation, no surprises, testable. This is the right way 95% of the time.
The fix #2: global keyword (when you truly must)
count = 0
def increase():
global count # "I mean the GLOBAL count"
count += 1
increase()
increase()
print(count) # 2 — actually changed
global tells Python “assignments here target the file-level variable.” It works — and real programs do use it for genuinely global state (a config flag, a “logged in” marker). But every global is a hidden thread connecting distant code; use it sparingly.
LEGB — the lookup order
When Python meets a name, it searches four levels in order:
- L — Local: inside the current function
- E — Enclosing: inside an outer function (if functions nest)
- G — Global: at the file’s top level
- B — Built-in: Python’s own names (
print,len,True)
First match wins. Which explains this classic:
print = "hello" # you just shadowed the BUILT-IN print!
print(print) # "hello" — and print() is now broken
Shadowing built-ins (list = [1,2], sum = 0, print = ...) is legal but sabotages yourself. Avoid naming things list, sum, max, str, input, type.
Locals die with the function
def make_name():
local_name = "temporary"
make_name()
print(local_name) # NameError — local_name no longer exists
Locals are born on call and die at return. Anything you need after the function must be returned.
Common Errors & Fixes
UnboundLocalError— you assigned to a global’s name inside a function. Restructure with return, or declareglobal.- “My function didn’t change my variable” — assignment created a local. Return the new value and reassign outside.
NameErroron a variable from another function — locals don’t leak between functions. Pass it as a parameter.
✅ Checkpoint
- Can a function read a global? (Yes)
- Can it change a global without
global? (No — assignment creates a local) - What does LEGB stand for? (Local, Enclosing, Global, Built-in)
- What’s the professional alternative to
global? (Pass in, return out)
Next: lambda, map, and filter — one-line functions for one-line jobs.