datarekha
Python Medium Asked at AmazonAsked at GoogleAsked at Microsoft

Explain Python's LEGB scope rule with an example.

The short answer

Python resolves names by searching four scopes in order: Local, Enclosing, Global, then Built-in. The first match wins. Assignment in a scope always creates or modifies a name in that scope unless global or nonlocal overrides this.

How to think about it

LEGB usually arrives as a warm-up — a way to reach the real targets, closures and the global/nonlocal keywords. The acronym itself is easy. The part worth your breath is one subtle rule: the moment a name is assigned anywhere inside a function, Python treats it as local for the entire body — even on lines above the assignment.

First the four scopes, searched innermost to outermost:

  1. Local — the function currently running.
  2. Enclosing — any outer functions, for closures, searched inside-out.
  3. Global — the module’s namespace.
  4. Built-in — Python’s own names (len, range, print, …).

Reading a name walks L→E→G→B and stops at the first hit. Writing a name always targets Local, unless you say global or nonlocal.

A worked example

x = "global"

def outer():
    x = "enclosing"
    def inner():
        x = "local"
        print("inner sees:", x)      # L wins
    inner()
    print("outer sees:", x)          # E is innermost for outer()

outer()
print("module sees:", x)             # G

print()

# nonlocal lets an inner function rebind the enclosing variable
def make_counter():
    count = 0
    def inc():
        nonlocal count
        count += 1
        return count
    return inc

c = make_counter()
print("counter:", c(), c(), c())

print()

# Built-ins live in the B layer — and can be shadowed (don't do this!)
print("len is built-in:", len([1, 2, 3]))
len = lambda x: "oops"
print("shadowed len:", len([1, 2, 3]))
del len                              # remove the shadow
print("restored len:", len([1, 2, 3]))
inner sees: local
outer sees: enclosing
module sees: global

counter: 1 2 3

len is built-in: 3
shadowed len: oops
restored len: 3

Each print resolved x from its own scope outward — three different answers for one name. The counter shows nonlocal at work, and the len block is a cautionary tale: assigning len at module level shadows the built-in everywhere below it, and only deleting the shadow brings the real one back.

The classic trap — UnboundLocalError

counter = 0
def increment():
    counter += 1     # UnboundLocalError: counter referenced before assignment

Python sees counter += 1, decides counter is local for the whole function, and then the read half of += runs before any local value exists — so it raises. The fix is to declare intent: global counter for a module variable, nonlocal count for an enclosing one.

Learn it properly Functions

Keep practising

All Python questions

Explore further

Skip to content