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!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 GalvanReading 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 changedPython'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) # 1Pass 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 changedglobal 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:
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 brokenShadowing 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 existsLocals are born on call and die at return. Anything you need after the function must be returned.
Common Errors & Fixes
global.✅ Checkpoint
global? *(No — assignment creates a local)*global? *(Pass in, return out)*Next: lambda, map, and filter — one-line functions for one-line jobs.