nonlocal
The nonlocal keyword lets you declare that a variable inside a nested function refers to a variable in an enclosing function scope, not a local variable. This allows you to modify variables from an outer (but non-global) function.
Syntax
nonlocal variable_name
You can declare multiple nonlocal variables by separating them with commas:
nonlocal x, y, z
How It Works
When you define a function inside another function, the inner function can read variables from the outer function by default. However, to modify those variables, you must declare them as nonlocal:
# Without nonlocal - creates a new local variable
def outer():
x = 10
def inner():
x = 20 # This creates a NEW local variable
print(x) # 20
inner()
print(x) # 10 (outer x unchanged)
# With nonlocal - modifies the outer function variable
def outer():
x = 10
def inner():
nonlocal x
x = 20 # This modifies the outer x
print(x) # 20
inner()
print(x) # 20 (outer x modified)
outer()
# Output:
# 20
# 20
Common Use Cases
Closures that modify captured variables
def create_counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
counter = create_counter()
print(counter()) # 1
print(counter()) # 2
print(counter()) # 3
Factory functions
def make_multiplier(factor):
def multiplier(n):
nonlocal factor
return n * factor
return multiplier
double = make_multiplier(2)
print(double(5)) # 10
triple = make_multiplier(3)
print(triple(5)) # 15
State in nested functions
def player(name):
score = 0
def win(points):
nonlocal score
score += points
return f"{name}: {score}"
return win
game = player("Alice")
print(game(10)) # Alice: 10
print(game(5)) # Alice: 15
nonlocal vs global
| Aspect | nonlocal | global |
|---|---|---|
| Scope | Enclosing function | Module level |
| Requires nested function | Yes | No |
| Common use | Closures | Module configuration |
x = "global"
def outer():
x = "outer"
def inner():
global x # Modifies module-level x
x = "changed globally"
inner()
print(x) # "outer" (unchanged)
def outer2():
x = "outer"
def inner():
nonlocal x # Modifies outer function x
x = "changed locally"
inner()
print(x) # "changed locally"
outer()
outer2()
# Output:
# outer
# changed locally
Best Practice
While nonlocal is useful for closures, consider these alternatives:
Using a mutable container
def create_counter():
state = [0] # Mutable list
def increment():
state[0] += 1
return state[0]
return increment
Using classes
class Counter:
def __init__(self):
self.count = 0
def increment(self):
self.count += 1
return self.count
Gotchas
Forgetting nonlocal
def outer():
value = 10
def inner():
value += 1 # UnboundLocalError!
inner()
This raises UnboundLocalError because Python sees an assignment and treats value as local. Use nonlocal value.
Trying to use nonlocal in the same scope
value = 10
def same_scope():
nonlocal value # SyntaxError!
nonlocal only works inside nested functions, not at the same level as the variable.
Shadowing global variables
x = 1
def outer():
x = 2
def inner():
nonlocal x
x = 3
inner()
print(x) # 3
outer()
print(x) # 1 (global x unchanged)
See Also
- global-keyword — access module-level variables
- def-keyword — define functions
- lambda-keyword — anonymous functions