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.
