DEV Community

Python-T Point
Python-T Point

Posted on • Originally published at pythontpoint.in

🐍 When to use global vs nonlocal python

💡 Global vs nonlocal — When to use global vs nonlocal python

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 nonlocal to modify a global variable?
  • Is it possible to declare a variable as both global and nonlocal?
  • Do global and nonlocal affect 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()
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

What this does:

  • counter = 0 : defines a module‑level integer.
  • global counter : tells the compiler that assignments target the global counter instead 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
Enter fullscreen mode Exit fullscreen mode

🛠 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
Enter fullscreen mode Exit fullscreen mode

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 count instead 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
Enter fullscreen mode Exit fullscreen mode




🔁 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; nonlocal for closures, global for 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 global statement — detailed language reference: docs.python.org
  • Official Python documentation on the nonlocal statement — explains enclosing scope semantics: docs.python.org

Top comments (0)