Script Valley
Python: Complete Language Course
Functions and Functional PatternsLesson 3.4

Python closures and the scope chain

LEGB scope rule, local scope, enclosing scope, global scope, built-in scope, global keyword, nonlocal keyword, closures

Scope in Python

Python resolves names using LEGB order: Local → Enclosing → Global → Built-in. The interpreter searches each level and uses the first match found.

x = "global"

def outer():
    x = "enclosing"
    def inner():
        x = "local"
        print(x)  # local
    inner()
    print(x)  # enclosing

outer()
print(x)  # global

global and nonlocal

count = 0
def increment():
    global count
    count += 1

increment()
print(count)  # 1

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

counter = make_counter()
print(counter())  # 1
print(counter())  # 2

Closures

A closure is a function that remembers variables from its enclosing scope after that scope has finished. make_counter above returns a closure — each call to counter() uses the same n from the enclosing make_counter call. Closures are the foundation of decorators and factory functions.

The LEGB rule resolves every name lookup in Python. Understanding it prevents confusing bugs where a variable seems to have the wrong value. Functions that rely on global state are harder to test because the test must set up the global before calling the function. Prefer passing data as arguments and returning results. Closures enable factory functions that return specialised versions of behaviour — a common pattern in configuration and middleware. The nonlocal keyword is needed specifically when a closure needs to rebind (not just read) a variable from its enclosing scope.

Up next

Python decorators — how they work and how to write one

Sign in to track progress