Advertisement
⚙️ Functions

Python Scope – Local, Global, Enclosing, and Built-in (LEGB Rule)

Scope determines where a variable is visible and accessible in your code. Python follows the LEGB rule to look up variable names: Local → Enclosing → Global → Built-in. Misunderstanding scope causes some of the most confusing bugs in Python.

⏱️ 20 min read 🎯 Intermediate 📅 Updated 2026

The LEGB Rule

When Python encounters a variable name, it searches in this order: Local (inside the current function), Enclosing (outer function in nested functions), Global (module-level), Built-in (Python's built-in names like len, print, range).

Python
x = "global"     # G - global scope

def outer():
    x = "enclosing"  # E - enclosing scope
    def inner():
        x = "local"  # L - local scope
        print(x)     # Finds "local" first
    inner()
    print(x)         # Finds "enclosing"

outer()
print(x)             # Finds "global"
▶ Output
local enclosing global

The global Keyword

To modify a global variable inside a function, declare it with the global keyword.

Python
count = 0  # Global

def increment():
    global count   # Tell Python to use the global count
    count += 1

increment()
increment()
increment()
print(count)  # 3 - global was modified
▶ Output
3
💡
Tip

Using global is generally discouraged. Prefer passing values as arguments and returning them instead.

Advertisement

The nonlocal Keyword (Closures)

In nested functions, nonlocal refers to the enclosing function's variable (not global).

Python
def make_counter():
    count = 0
    def counter():
        nonlocal count   # Refers to outer function's count
        count += 1
        return count
    return counter

my_counter = make_counter()
print(my_counter())  # 1
print(my_counter())  # 2
print(my_counter())  # 3
▶ Output
1 2 3

Built-in Scope

Built-in scope contains Python's built-in functions and constants. Be careful not to shadow them with your own variable names.

Python
# ❌ Don't shadow built-ins!
list = [1, 2, 3]   # Overwrites the built-in list!
print = "hi"       # Overwrites print! Now you can't print!

# ✅ Use descriptive names
my_list = [1, 2, 3]
message = "hi"
print(message)     # Works fine

The LEGB Rule: Where Python Looks Up a Name

When you use a variable, Python searches four scopes in order — Local, Enclosing, Global, Built-in — and uses the first match. Understanding LEGB explains most "why is this variable that value?" confusion.

x = "global"
def outer():
    x = "enclosing"
    def inner():
        print(x)     # finds "enclosing" (E) before "global" (G)
    inner()

Assigning needs global / nonlocal

Reading an outer variable is automatic; rebinding one is not. Assign to a name inside a function and Python treats it as local unless you declare otherwise:

count = 0
def bump():
    global count      # without this, "count += 1" raises UnboundLocalError
    count += 1

def make_counter():
    n = 0
    def inc():
        nonlocal n    # rebind the enclosing n, not create a new local
        n += 1; return n
    return inc

Common trap: count += 1 without global fails because the += makes count a local, then reads it before assignment. Prefer returning values over mutating globals — globals make code hard to test and reason about.

🏋️ Practical Exercise

Explore variable scope:

  1. Define a variable inside a function and confirm it is not visible outside.
  2. Read a global variable from inside a function (no keyword needed).
  3. Use the global keyword to modify a module-level variable from a function.
  4. Create a closure with an inner function and use nonlocal to update an enclosing variable.

🔥 Challenge Exercise

Build a counter factory: a function make_counter() that returns an inner function which, each time it is called, increments and returns a count. Use nonlocal so the inner function updates the enclosing variable. Create two independent counters and show they don’t interfere. Then write a small example that demonstrates the full LEGB lookup order with a name defined at several levels.

📋 Summary

  • Scope determines where a name is visible; Python resolves names using the LEGB rule.
  • LEGB = Local → Enclosing → Global → Built-in, searched in that order.
  • Assigning to a name inside a function creates a new local unless declared global or nonlocal.
  • global lets a function rebind a module-level variable.
  • nonlocal lets a nested function rebind a variable in the enclosing (but non-global) scope.
  • A closure is an inner function that remembers variables from its enclosing scope.

Interview Questions on Scope

  • What is variable scope in Python?
  • What is the LEGB rule?
  • What does the global keyword do?
  • What does the nonlocal keyword do and how does it differ from global?
  • What is a closure?
  • Why can you read a global variable in a function but not assign to it without global?
  • What is the built-in scope?

FAQ

What does the LEGB rule mean? +

It is the order Python searches for a name: Local (inside the current function), Enclosing (any outer functions), Global (module level), and Built-in (Python’s own names like len). The first match wins.

What is the difference between global and nonlocal? +

global binds a name to the module-level scope, letting a function modify a top-level variable. nonlocal binds to the nearest enclosing function scope, used in closures to modify a variable in an outer (but not global) function.

Why can I read a global but not change it without global? +

Reading falls back through LEGB and finds the global. But assigning to a name inside a function makes Python treat it as a new local for the whole function, shadowing the global — unless you declare global first.

What is a closure? +

A closure is a nested function that captures and remembers variables from its enclosing scope even after that outer function has finished. It is the basis for decorators and factory functions like a counter generator.