Tech With Galvan

Scope: Local vs Global (LEGB in 5 Minutes)

15 min • Beginner • Module 7

Understand where variables live and why names clash or vanish.

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:

  1. L — Local: inside the current function
  2. E — Enclosing: inside an outer function (if functions nest)
  3. G — Global: at the file’s top level
  4. 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 declare global.
  • “My function didn’t change my variable” — assignment created a local. Return the new value and reassign outside.
  • NameError on a variable from another function — locals don’t leak between functions. Pass it as a parameter.

✅ Checkpoint

  1. Can a function read a global? (Yes)
  2. Can it change a global without global? (No — assignment creates a local)
  3. What does LEGB stand for? (Local, Enclosing, Global, Built-in)
  4. What’s the professional alternative to global? (Pass in, return out)

Next: lambda, map, and filter — one-line functions for one-line jobs.