Python 101Free
FUNCTIONS

Scope and Closures

Where names live and how nested functions remember them.

SECTION 01

The LEGB rule

When Python sees a name in a function, it looks for the binding in four scopes, in order. Local first (the function itself), then Enclosing (any outer function that wraps this one), then Global (the module's top level), then Builtin (len, range, print, and friends).

This is the LEGB rule, and it determines what every name resolves to. The first scope that has a binding wins, the rest are not consulted.

Most confusion about scope comes from accidentally shadowing a builtin (def list(...) is a bad idea) or trying to assign to a global from inside a function (which creates a local instead, unless you use global). The rule is mechanical, but the consequences take a minute to digest.

SECTION 02

Closures

When an inner function references a name from an enclosing function, Python keeps that name alive even after the outer function returns. The inner function carries the binding with it. This is a closure.

The canonical example is a function factory: make_counter() returns an inner step function that increments a counter n defined in the enclosing scope. Each call to make_counter creates a fresh n, and each step returned has its own private copy.

Closures are how you express "a function that remembers some state" without introducing classes. They are also why decorators, lazy iterators, and small DSLs feel natural to write in Python.

python
def make_counter():
    n = 0
    def step():
        nonlocal n
        n += 1
        return n
    return step

c = make_counter()
c(), c(), c()   # (1, 2, 3)
← PREVIOUS
Arguments
NEXT →
Higher Order Functions