Python nonlocal keyword: scope and closures
Learn how the python nonlocal keyword works, when to use it in nested functions, and how it differs from global and local scope.
The python nonlocal keyword lets a nested function rebind a variable that exists in an enclosing function's scope. Without it, assignment inside the nested function creates a new local variable instead of updating the outer one. This distinction is easy to miss and produces subtle bugs.
What nonlocal actually changes
In Python, assignment to a name inside a function makes that name local to the function by default. When a function is nested inside another, the inner function's local scope is separate from the outer function's scope. The nonlocal declaration tells the interpreter that a particular name refers to a variable bound in the nearest enclosing function scope, excluding globals. It does not create a new variable; it rebinds an existing one.
def outer(): count = 0 def inner(): nonlocal count count += 1 return count return inner
Here count is defined in outer. The inner function uses nonlocal count so that count += 1 updates the outer variable rather than attempting to create a local count. Without the declaration, Python would raise UnboundLocalError because the assignment makes count local to inner, and it is read before being assigned.
How nonlocal differs from global
The global keyword rebinds a module-level name. nonlocal works only within nested functions and binds to a name in an enclosing function's scope, not the module scope. The two keywords are not interchangeable.
x = 10 def outer(): x = 5 def inner(): global x # refers to module-level x x += 1 inner() print(x) # 5, unchanged print(x) # 11
With nonlocal, the same example updates the x in outer:
def outer(): x = 5 def inner(): nonlocal x x += 1 inner() print(x) # 6
Use global when you intend to modify a module-level binding. Use nonlocal when you need to modify a binding in an enclosing function. Mixing them in the same nested function is allowed but rarely necessary.
Practical use: closures with mutable state
The most common use of nonlocal is building closures that retain state between calls. A closure is a function that captures variables from its enclosing scope. The nonlocal keyword makes that captured state writable.
def make_counter(start=0): value = start def increment(step=1): nonlocal value value += step return value return increment counter = make_counter(10) print(counter()) # 11 print(counter(5)) # 16
This pattern is useful for stateful callbacks, lazy initialization, or memoization. The state lives in the closure and is isolated from other instances of the same factory function.
Common mistake: forgetting nonlocal
If you assign to a variable inside a nested function without declaring it nonlocal, Python treats it as a new local variable. The outer variable remains unchanged, and you may not see an error until you read the variable before assignment.
def outer(): items = [] def add(item): items = items + [item] # UnboundLocalError: items referenced before assignment return items return add
The error occurs because items is local due to the assignment, and the right-hand side reads it before it has a value. Using nonlocal items fixes this. Alternatively, avoid rebinding by mutating the list in place:
def outer(): items = [] def add(item): items.append(item) # no nonlocal needed, mutation only return items return add
Mutation does not require nonlocal because you are not reassigning the name items. This distinction is a frequent source of confusion.
When to avoid nonlocal
nonlocal adds a hidden dependency on the enclosing function's state. This can make the code harder to reason about, especially when the closure is long or the outer function has many variables. In many cases, a small class or a mutable container is clearer.
class Counter: def __init__(self, start=0): self.value = start def increment(self, step=1): self.value += step return self.value
A class makes the state explicit and avoids the scope magic. Use nonlocal when the state is trivial and the closure is short. For more complex state, prefer a class or a named tuple with mutable fields.
Another alternative is to store state in a mutable object like a list or dictionary. This avoids nonlocal entirely but obscures intent. The nonlocal version is often more readable because it names the variable directly.
Runtime behavior and debugging
nonlocal does not introduce any runtime overhead beyond normal variable access. The binding is resolved at compile time, and the generated bytecode references the cell variable directly. There is no dictionary lookup at runtime for the enclosing scope.
Debugging closures that use nonlocal can be tricky because the state is not visible in the local frame of the inner function. Tools like inspect can help, but a simpler approach is to add logging or to temporarily return the state for inspection. The closure's __closure__ attribute contains the captured cell objects, and you can read their cell_contents.
counter = make_counter(10) print(counter.__closure__[0].cell_contents) # 10
This is useful for understanding what the closure holds, but it is not something you would use in production code.
Maintainability tradeoffs
Using nonlocal in a deeply nested structure can make the data flow implicit. A reader must trace which enclosing function defines the variable and where it is modified. This is acceptable for a two-level nesting, but beyond that, the code becomes difficult to follow.
A more maintainable pattern is to keep the closure small and the state minimal. If the outer function has many variables that need to be modified from inner functions, consider refactoring into a class. The class makes the state a first-class attribute, and methods can modify it without any scope declaration.
Another consideration is that nonlocal only works inside a nested function. You cannot use it at module level or in a class body. Attempting to do so raises a SyntaxError. This constraint is by design: the keyword exists specifically for enclosing function scopes.
When you need to share state across multiple nested functions, each one must declare nonlocal for the same variable. This repetition is fine for one or two functions, but it can become noisy. A class with shared attributes often reads better in that situation.
Finally, be aware that nonlocal does not affect the variable's lifetime. The variable lives as long as the closure does. If the outer function returns and the closure is still referenced, the captured variable persists in memory. This is the same behavior as any closure, and it is usually what you want for stateful callbacks.
In summary, the python nonlocal keyword is a precise tool for rebinding variables in enclosing scopes. Use it when you need a closure with writable state and the nesting is shallow. For deeper nesting or more complex state, a class is often a clearer choice. Understanding the distinction between rebinding and mutation, and knowing when not to use nonlocal, is more valuable than memorizing the syntax.