Python Basics — global & nonlocal
global & nonlocal
Python provides two keywords — global and nonlocal — that let you modify variables in outer scopes. While useful in specific situations, they should be used sparingly.
The global Keyword
By default, assigning to a variable inside a function creates a local variable. The global keyword tells Python to use the module-level variable instead:
count = 0
def increment():
global count # tells Python: use the global count
count += 1
increment()
increment()
increment()
print(count) # 3
Without global:
count = 0
def increment():
count += 1 # UnboundLocalError!
# increment() # Error
Declaring Multiple Globals
x = 0
y = 0
def reset():
global x, y
x = 0
y = 0
Risks of global
# ❌ Hard to track — who changed this?
settings = {"debug": False}
def enable_debug():
global settings
settings["debug"] = True
def disable_debug():
global settings
settings["debug"] = False
# Scattered global modifications make code hard to debug
| Risk | Example |
|---|---|
| Hidden dependencies | Function behavior depends on external state |
| Hard to test | Must set up global state before testing |
| Race conditions | Multiple threads modifying the same global |
| Unexpected side effects | Calling a function changes things elsewhere |
The nonlocal Keyword
nonlocal does for enclosing function scopes what global does for module scope:
def outer():
count = 0
def inner():
nonlocal count # modify the enclosing scope variable
count += 1
inner()
inner()
inner()
print(count) # 3
outer()
Without nonlocal:
def outer():
count = 0
def inner():
count += 1 # UnboundLocalError!
inner()
# outer() # Error
nonlocal with Multiple Nested Levels
nonlocal finds the nearest enclosing scope that has the variable:
def level1():
x = "level1"
def level2():
x = "level2"
def level3():
nonlocal x # binds to level2's x (nearest)
x = "modified"
level3()
print(f"level2: {x}") # modified
level2()
print(f"level1: {x}") # level1 — unaffected
level1()
# Output:
# level2: modified
# level1: level1
When to Use (and When to Avoid)
Acceptable Uses
Counters in closures:
def make_counter():
count = 0
def counter():
nonlocal count
count += 1
return count
return counter
c = make_counter()
print(c()) # 1
print(c()) # 2
print(c()) # 3
Simple flag toggles:
def make_toggle():
state = False
def toggle():
nonlocal state
state = not state
return state
return toggle
switch = make_toggle()
print(switch()) # True
print(switch()) # False
When to Avoid
| Instead of | Use |
|---|---|
global for sharing state |
Pass arguments and return values |
global for configuration |
Use a config dictionary or class |
nonlocal for complex state |
Use a class with attributes |
| Mutable global variables | Constants (UPPER_CASE) that don’t change |
Alternatives to global
Return Values (Preferred)
# ❌ Using global
total = 0
def add_global(n):
global total
total += n
add_global(5)
add_global(3)
print(total) # 8
# ✅ Using return values
def add_pure(total, n):
return total + n
total = 0
total = add_pure(total, 5)
total = add_pure(total, 3)
print(total) # 8
Using a Class
# ✅ Better for complex state
class Counter:
def __init__(self):
self.count = 0
def increment(self):
self.count += 1
return self.count
c = Counter()
print(c.increment()) # 1
print(c.increment()) # 2
Using a Mutable Container (Workaround)
# Works without nonlocal — mutating, not reassigning
def outer():
state = {"count": 0}
def inner():
state["count"] += 1 # mutating is allowed without nonlocal
inner()
inner()
print(state["count"]) # 2
outer()
global vs nonlocal — Comparison
| Feature | global |
nonlocal |
|---|---|---|
| Targets | Module-level scope | Nearest enclosing function scope |
| Works in | Any function | Only nested functions |
| Common use | Module-wide state | Closures and nested function state |
| Best avoided? | Usually yes | More acceptable in closures |
Summary
globallets you modify module-level variables inside functionsnonlocallets you modify enclosing function variables in nested functions- Both should be used sparingly — prefer passing arguments and returning values
- For complex state, use a class instead of
globalornonlocal - If you find yourself using
globaloften, rethink your design
Next up: Closures →