💡 Global vs nonlocal — When to use global vs nonlocal python
Choosing the wrong scope modifier in Python creates bugs that hide behind the interpreter’s name‑resolution rules.
📑 Table of Contents
- 💡 Global vs nonlocal — When to use global vs nonlocal python
- 🧠 Scopes — Understanding Names
- 🔧 Global Statement — When to use global
- 🛠 Modifying Module State
- ⚙️ Nonlocal Statement — When to use nonlocal
- 🔁 Closure Variable Updates
- 📊 Global vs Nonlocal — Choosing the right modifier
- 🟩 Final Thoughts
- ❓ Frequently Asked Questions
- Can I use
nonlocalto modify a global variable? - Is it possible to declare a variable as both
globalandnonlocal? - Do
globalandnonlocalaffect performance? - 📚 References & Further Reading
🧠 Scopes — Understanding Names
Python resolves identifiers using a fixed hierarchy of scopes, and this hierarchy determines where a name is read from or written to.
LEGB (Local, Enclosing, Global, Built‑in) is the order the interpreter follows when searching for a name. A local scope belongs to the current function, an enclosing scope is any function that contains the current one, global refers to the module namespace, and built‑in is the interpreter’s default namespace.
# example.py
x = 10 # global variable def outer(): y = 20 # enclosing variable def inner(): z = 30 # local variable print(x, y, z) # LEGB lookup inner() outer()
What this does:
- x = 10 : creates a module‑level variable (global scope).
-
y = 20 : lives in the enclosing function
outer. -
z = 30 : is local to
inner. - print(x, y, z) : resolves each name according to LEGB.
Because the interpreter never searches a scope that is not part of the LEGB chain, a name can only be written to a scope that is explicitly declared.
Key point: The scope hierarchy is immutable; you cannot create a new scope level at runtime.
🔧 Global Statement — When to use global
The global statement binds an identifier to the module‑level namespace. At compile time the name is added to the function’s co_names list, and assignments compile to STORE_GLOBAL bytecode, which updates the module’s dictionary directly.
# global_example.py
counter = 0 # global variable def increment(): global counter # declare intent to write to the module variable counter += 1 def show(): print("counter =", counter) increment()
increment()
show()
What this does:
- counter = 0 : defines a module‑level integer.
-
global counter : tells the compiler that assignments target the global
counterinstead of creating a new local binding. - counter += 1 : mutates the global variable in place.
- show() : reads the updated global value.
Why this, not the obvious alternative: Returning the new value from increment would require the caller to reassign the variable each time, adding boilerplate when the intent is simply to maintain shared state.
Bytecode illustration (Python 3.11): (Also read: ☁️ Prometheus vs CloudWatch for MinIO — Which One Should You Use?)
>>> import dis
>>> dis.dis(increment) 4 0 LOAD_GLOBAL 0 (counter) 2 LOAD_CONST 1 (1) 4 INPLACE_ADD 6 STORE_GLOBAL 0 (counter) 8 LOAD_CONST 0 (None) 10 RETURN_VALUE
🛠 Modifying Module State
The global statement is appropriate when a function needs to update configuration flags, caches, or counters that are truly module‑wide. It should not be used inside nested functions that only need to affect a parent function’s variable. (Also read: 🤔 Ansible handlers vs tasks when to choose each)
Key point: Use global only for module‑level state; avoid it for local closures. (Also read: 🐍 CI/CD Python App Service vs AKS — Which One Should You Use?)
⚙️ Nonlocal Statement — When to use nonlocal
The nonlocal statement binds a name to the nearest enclosing function scope. During compilation the variable is stored in a cell object; assignments compile to STORE_DEREF, which updates the same cell across all inner functions that reference it.
# nonlocal_example.py
def make_counter(): count = 0 # enclosing variable def step(): nonlocal count # refer to the enclosing 'count' count += 1 return count return step counter = make_counter()
print(counter()) # 1
print(counter()) # 2
What this does:
-
count = 0 : creates a variable in the enclosing scope of
step. -
nonlocal count : tells the compiler to look one level up for
countinstead of creating a new local binding. - count += 1 : mutates the enclosing variable.
- return step : returns the inner function, forming a closure that carries the mutable state.
Why this, not the obvious alternative: Using a mutable container (e.g., a list) would work but obscures intent; nonlocal makes the relationship explicit and avoids accidental re‑binding.
Bytecode illustration (Python 3.11): (More onPythonTPoint tutorials)
>>> import dis
>>> dis.dis(make_counter.code.co_consts[1]) # disassemble step 6 0 LOAD_DEREF 0 (count) 2 LOAD_CONST 1 (1) 4 INPLACE_ADD 6 STORE_DEREF 0 (count) 8 LOAD_DEREF 0 (count) 10 RETURN_VALUE
🔁 Closure Variable Updates
When building decorators, factories, or simple stateful generators, nonlocal provides a clean way to keep mutable state without exposing it at the module level.
Key point: nonlocal only works for variables in an enclosing function, not for module globals.
📊 Global vs Nonlocal — Choosing the right modifier
This section compares the two statements and presents a decision matrix that helps you decide which one to use in a given situation.
| Aspect | global | nonlocal |
|---|---|---|
| Target scope | module (top‑level) namespace | nearest enclosing function |
| Visibility | accessible from any function in the module | visible only to nested functions |
| Typical use case | shared counters, configuration flags | stateful closures, decorators |
| Risk of accidental mutation | high – any function can change the variable | lower – only inner functions can affect it |
| Supported Python version | since Python 1.0 | since Python 3.0 |
According to the official Python documentation, global and nonlocal are the only statements that alter the default LEGB resolution for assignments.
Why this, not the obvious alternative: Declaring a variable as global when you only need to modify an enclosing variable leads to unnecessary coupling across the module, while using nonlocal for module‑level state makes the code harder to understand because the variable’s scope is hidden.
Key point: Prefer nonlocal for localized state in nested functions and reserve global for truly module‑wide data.
Use the smallest scope that conveys intent;
nonlocalfor closures,globalfor module‑level state.
🟩 Final Thoughts
Understanding the precise mechanics of name resolution lets you write code that is both clear and safe. The global statement lifts a name to the module level, making it visible everywhere in the file, while nonlocal targets the nearest enclosing function, preserving encapsulation.
When you decide between them, ask yourself: “Is the state needed across the entire module or just within a closure?” If the answer is the former, global is appropriate; if the latter, nonlocal keeps the mutation local and the intent obvious.
❓ Frequently Asked Questions
Can I use nonlocal to modify a global variable?
No. nonlocal only searches enclosing function scopes, never the module scope. To modify a global variable from a nested function you must use global.
Is it possible to declare a variable as both global and nonlocal?
A single name cannot be declared with both statements in the same function; the compiler will raise a SyntaxError because the scopes conflict.
Do global and nonlocal affect performance?
Both statements add a small compile‑time lookup cost, but the runtime impact is negligible compared to the clarity they provide. The primary concern is logical correctness, not speed.
💡 Want to practise this hands-on? DigitalOcean gives new accounts $200 free credit for 60 days — enough to spin up a full Linux/Docker/Kubernetes environment at no cost.
📚 Recommended reading: Best DevOps & cloud books on Amazon — from Linux fundamentals to Kubernetes in production, curated for working engineers.
📚 References & Further Reading
- Official Python documentation on the
globalstatement — detailed language reference: docs.python.org - Official Python documentation on the
nonlocalstatement — explains enclosing scope semantics: docs.python.org

Top comments (0)