Python Basics — LEGB Rule
The LEGB Rule
When Python encounters a variable name, it searches for it in a specific order known as the LEGB rule. Understanding this order is key to predicting how your code behaves.
What Is LEGB?
LEGB stands for the four scopes Python checks, in order:
| # | Scope | Description | Example |
|---|---|---|---|
| L | Local | Inside the current function | Function variables |
| E | Enclosing | Inside any enclosing (outer) functions | Nested function variables |
| G | Global | At the module (file) level | Module-level variables |
| B | Built-in | Python’s built-in names | print, len, range |
Python stops searching as soon as it finds the name in any scope.
Visual Diagram
┌──────────────────────────────────────────┐
│ Built-in (B) │
│ print, len, range, int, str, ... │
│ ┌────────────────────────────────────┐ │
│ │ Global (G) │ │
│ │ Module-level variables │ │
│ │ ┌──────────────────────────────┐ │ │
│ │ │ Enclosing (E) │ │ │
│ │ │ Outer function variables │ │ │
│ │ │ ┌────────────────────────┐ │ │ │
│ │ │ │ Local (L) │ │ │ │
│ │ │ │ Current function vars │ │ │ │
│ │ │ └────────────────────────┘ │ │ │
│ │ └──────────────────────────────┘ │ │
│ └────────────────────────────────────┘ │
└──────────────────────────────────────────┘
↑ Python searches from inside out
Examples at Each Level
Local Scope (L)
def greet():
name = "Local" # L — found first
print(name)
greet() # Local
Enclosing Scope (E)
def outer():
name = "Enclosing"
def inner():
print(name) # Not in L, found in E
inner()
outer() # Enclosing
Global Scope (G)
name = "Global"
def outer():
def inner():
print(name) # Not in L, not in E, found in G
inner()
outer() # Global
Built-in Scope (B)
def outer():
def inner():
print(len("hello")) # len is not in L, E, or G — found in B
inner()
outer() # 5
LEGB in Action — All Four Levels
x = "global"
def outer():
x = "enclosing"
def inner():
x = "local"
print(x) # Finds "local" (L)
inner()
print(x) # Finds "enclosing" (E for inner, L for outer)
outer()
print(x) # Finds "global" (G)
# Output:
# local
# enclosing
# global
Now remove the local assignment:
x = "global"
def outer():
x = "enclosing"
def inner():
print(x) # Not in L → checks E → finds "enclosing"
inner()
outer()
# Output: enclosing
Shadowing Built-ins (Don’t Do This!)
# ❌ Bad — shadows the built-in len
len = 42
# print(len("hello")) # TypeError: 'int' object is not callable
# ✅ Fix — delete the shadowing name
del len
print(len("hello")) # 5
Common names accidentally shadowed by beginners:
| Built-in | Often Misused As |
|---|---|
list |
list = [1, 2, 3] |
dict |
dict = {"a": 1} |
str |
str = "hello" |
type |
type = "admin" |
input |
input = "text" |
id |
id = 42 |
Scope Inspection with dir() and vars()
x = "global"
def show_scopes():
y = "local"
print("Local vars:", vars()) # Shows local namespace
print("'y' in locals:", 'y' in locals())
print("'x' in globals:", 'x' in globals())
show_scopes()
# Local vars: {'y': 'local'}
# 'y' in locals: True
# 'x' in globals: True
Check built-in names:
import builtins
print(dir(builtins)[:10])
# ['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', ...]
Common Mistakes
# ❌ Mistake: assuming assignment reads the global first
total = 100
def add(n):
total = total + n # UnboundLocalError!
return total
# Python sees `total = ...` → marks total as local → right side reads local total
# which doesn't exist yet → error
Summary
| Scope | Where | Lifetime |
|---|---|---|
| Local (L) | Inside current function | While function runs |
| Enclosing (E) | Inside outer function | While outer function runs |
| Global (G) | Module level | While module is loaded |
| Built-in (B) | Python interpreter | Always available |
Key rule: Python searches L → E → G → B and uses the first match it finds.
Next up: global & nonlocal →