nonlocal: The Forgotten Python Keyword Almost Nobody Understands
Why nonlocal exists, how it works with closures, and why most developers avoid it.

There’s a Python keyword that almost nobody talks about.
It’s not flashy like async.
It’s not controversial like global.
It’s not trendy like match.
It’s nonlocal.
And most Python developers go years without ever using it.
So why does it exist?
The Problem nonlocal Solves
To understand nonlocal, you need to understand closures.
A closure happens when:
A function is defined inside another function
The inner function remembers variables from the outer function
Example:
def outer():
x = 10
def inner():
print(x)
return inner
func = outer()
func()
Output:
10
The inner function remembers x, even though outer() has finished running.
That’s a closure.
But here’s the problem.
What if you want to modify x inside inner()?
def outer():
x = 10
def inner():
x += 1
print(x)
return inner
This throws an error:
UnboundLocalError: local variable 'x' referenced before assignment
Why?
Because Python thinks x inside inner() is a new local variable.
It doesn’t know you meant the one in outer().
This is where nonlocal comes in.
What nonlocal Actually Does
nonlocal tells Python:
“The variable I’m about to use is not local to this function —
it exists in the nearest enclosing scope.”
Here’s the fixed version:
def outer():
x = 10
def inner():
nonlocal x
x += 1
print(x)
return inner
func = outer()
func()
func()
Output:
11
12
Now inner() modifies the x defined in outer().
That’s the entire purpose of nonlocal.
global vs nonlocal
People often confuse global and nonlocal.
They are completely different.
global
Refers to variables at the module level
Breaks function isolation
Affects the entire program
Example:
x = 5
def change():
global x
x = 20
Now every part of your program sees x = 20.
nonlocal
Refers to variables in the nearest enclosing function
Does NOT touch global scope
Works only inside nested functions
Think of it like:
global→ jump to the top of the filenonlocal→ jump one level up
Real-World Use Cases
Most people never need nonlocal.
But it shines in certain patterns.
1. Function State Without Classes
You can create stateful functions:
def counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
This behaves like a mini-object without creating a class.
2. Decorators That Track State
Decorators sometimes use nonlocal to modify outer variables without global state.
3. Controlled Mutation Inside Closures
In functional-style code, nonlocal lets you mutate just enough state without polluting global scope.
Why Almost No One Uses It
There are reasons.
1. It Makes Code Harder to Read
When a function modifies variables outside itself, it becomes harder to reason about.
You must mentally track scope levels.
In large systems, that gets dangerous.
2. Classes Are Cleaner
Instead of:
def counter():
count = 0
...
Most developers just write:
class Counter:
def __init__(self):
self.count = 0
It’s more explicit. More scalable.
3. Nested Functions Are Rare in Big Codebases
In production systems, deep nesting is often avoided.
That reduces the need for nonlocal.
When You Should Use It
Use nonlocal when:
You need closure-based state
You’re writing decorators
You want lightweight encapsulation
You understand scope rules deeply
Avoid it when:
Working in large teams
Building complex systems
Clarity matters more than cleverness
The Bigger Picture
Python’s scope hierarchy is:
Local
Enclosing (nonlocal)
Global
Built-in
nonlocal exists to make that second layer explicit.
Without it, closures could read outer variables — but never modify them.
So nonlocal is not a mistake.
It’s a precision tool.
Just one that most people never take out of the toolbox.

