Python Basics — LEGB Rule

3 minute read

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 →

Updated: