Scope and Namespace
Scope and Namespace
Section titled “Scope and Namespace”Introduction
Section titled “Introduction”Scope determines where a variable is accessible. Python follows the LEGB rule: Local → Enclosing → Global → Built-in.
LEGB Lookup Order
Section titled “LEGB Lookup Order”flowchart TB Code["🔍 Variable lookupx = 5"] --> Local["1️⃣ LocalInside current function"] Local -->|"Not found"| Enclosing["2️⃣ EnclosingOuter function"] Enclosing -->|"Not found"| Global["3️⃣ GlobalModule level"] Global -->|"Not found"| Builtin["4️⃣ Built-inPython built-insprint, len, range"] Builtin -->|"Not found"| Error["❌ NameError"]
Local -->|"✅ Found"| Use["Use the value"] Enclosing -->|"✅ Found"| Use Global -->|"✅ Found"| Use Builtin -->|"✅ Found"| Use
style Code fill:#7c3aed,color:#fff style Local fill:#059669,color:#fff style Enclosing fill:#4f46e5,color:#fff style Global fill:#f59e0b,color:#000 style Builtin fill:#dc2626,color:#fff style Error fill:#dc2626,color:#fff style Use fill:#059669,color:#fffLEGB Rule
Section titled “LEGB Rule”# LEGB: Local → Enclosing → Global → Built-in
built_in = "built-in" # This won't shadow print()
def outer(): enclosing = "enclosing"
def inner(): local = "local" print(local) # Finds 'local' in Local scope print(enclosing) # Finds 'enclosing' in Enclosing scope print(global_var) # Finds 'global_var' in Global scope print(len) # Finds 'len' in Built-in scope
inner()
global_var = "global"outer()Local Scope
Section titled “Local Scope”def my_func(): x = 10 # Local variable — only accessible inside the function print(x)
my_func() # 10# print(x) # NameError: name 'x' is not definedGlobal Scope
Section titled “Global Scope”x = 100 # Global variable
def my_func(): print(x) # Can READ global
my_func() # 100The global Keyword
Section titled “The global Keyword”counter = 0
def increment(): global counter # Declare intent to modify global counter += 1
increment()increment()print(counter) # 2The nonlocal Keyword
Section titled “The nonlocal Keyword”def outer(): count = 0
def inner(): nonlocal count # Modify enclosing scope variable count += 1 return count
return inner
counter = outer()print(counter()) # 1print(counter()) # 2print(counter()) # 3Closures
Section titled “Closures”A closure is a function that remembers variables from its enclosing scope even after the outer function has finished executing.
def make_multiplier(factor): def multiply(x): return x * factor return multiply
double = make_multiplier(2)triple = make_multiplier(3)
print(double(5)) # 10print(triple(5)) # 15Best Practices
Section titled “Best Practices”- Minimize global variables — they make code harder to test and reason about
- Use
globalandnonlocalsparingly — they indicate design issues - Prefer passing parameters and returning values over shared mutable state
- Use closures for creating function factories and decorators
Practice Exercises
Section titled “Practice Exercises”Exercise 1: Write a function that creates a counter using closures (like counter() that returns 1, 2, 3… on each call).
Exercise 2: Create a function that limits how many times another function can be called.