Backend / Python core / Tricky questions / 26_nonlocal_vs_global.md

nonlocal vs global

Updated 3 min read source
On this page9
  1. The gotcha
  2. Minimal repro
  3. Reading vs writing
  4. global — assign to module-level
  5. nonlocal — assign to enclosing function scope
  6. What nonlocal does NOT reach
  7. Common pitfalls
  8. When to use which
  9. Interview angle

nonlocal vs global

The gotcha

Assigning to a name inside a function creates a local variable, even if a same-named variable exists in an enclosing or global scope. To assign to the outer one, you must declare global (module-level) or nonlocal (enclosing function).

Minimal repro

python
x = 0

def f():
    x = 1          # creates a NEW local x; module-level x is untouched
    print(x)       # 1

f()
print(x)           # 0  ← unchanged
python
def outer():
    x = 0
    def inner():
        # NEW local in inner; outer's x untouched
        x = 1
    inner()
    print(x)       # 0

Reading vs writing

Reading is easy — Python walks LEGB (Local → Enclosing → Global → Built-in):

python
x = 10
def f():
    # 10 — reads from global, no declaration needed
    print(x)

Writing implicitly creates a local. The compiler decides at compile time which names are local based on whether any assignment to that name appears anywhere in the function body. Even an assignment in unreachable code makes the name local for the whole function:

python
x = 10
def f():
    print(x)       # UnboundLocalError — `x` is local because of the assignment below
    if False:
        x = 99

global — assign to module-level

python
counter = 0

def increment():
    global counter
    counter += 1

increment()
print(counter)     # 1

Without global, counter += 1 is counter = counter + 1 → reads then assigns → local. The read fails because the local doesn’t have a value yet → UnboundLocalError.

nonlocal — assign to enclosing function scope

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

c = make_counter()
print(c(), c(), c())   # 1 2 3

Without nonlocal, the inner function creates its own n and the closure breaks. This pattern (counter, accumulator) is the classic motivation for nonlocal — added in Python 3.0.

What nonlocal does NOT reach

nonlocal only walks enclosing function scopes, not the module:

python
x = 10
def f():
    # SyntaxError: no binding for nonlocal 'x' found
    nonlocal x
    x = 20

For module-level, use global. For enclosing function, use nonlocal. There’s no syntax to skip levels — nonlocal binds to the nearest enclosing scope that has the name.

Common pitfalls

The “I just want to read the global” mistake:

python
config = {"debug": True}

def f():
    config["verbose"] = True   # no `global` needed — mutating the existing dict
    config = {}                # creates a new local; original untouched

Mutation works without global. Reassignment creates a local.

Closures over a loop variable:

python
funcs = []
for i in range(3):
    funcs.append(lambda: i)
# [2, 2, 2] — not what you want
print([f() for f in funcs])

i is captured by reference, not value. See Late-binding closuresnonlocal doesn’t fix this; default-argument or partial does.

Class bodies don’t behave like functions:

python
x = 1
class A:
    x = 2
    def f(self):
        print(x)   # 1 — class scope is NOT in LEGB

The class body is its own scope but methods can’t see it via LEGB. Use A.x or self.x.

When to use which

  • global — rarely. Module-level mutable state is usually a smell. Prefer passing values as arguments, or wrapping state in a class.
  • nonlocal — closures over local state (counters, builders, decorators that accumulate).
  • Neither — mutate a container the outer scope owns (config["x"] = 1). No declaration needed.

Interview angle 4

  • Q: “What’s the difference between global and nonlocal?” — module-level vs enclosing function scope.
  • Q: “Why does counter += 1 raise UnboundLocalError if there’s a global counter?” — += is read+write; the assignment makes it local; the read fails.
  • Follow-up: “Can you read a global without declaring it?” — yes; only writes need the declaration.
  • Follow-up: “What if you use nonlocal for a name not in any enclosing function?” — SyntaxError at compile time.

See Late-binding closures, Comprehension scope, Circular imports.