Back to Blog
Python

Python global vs nonlocal: Scope Rules Explained

python global vs nonlocal: Understand the difference between global and nonlocal in Python, how each affects variable assignment, and when to use them in nested functi...

Python scopingnonlocal keywordglobal keywordclosuresnested functions
Diagram showing a nested Python function with global and nonlocal scope bindings

In Python, the difference between global and nonlocal determines which scope an assignment inside a function targets. global binds a name to the module-level scope, while nonlocal binds it to the nearest enclosing function scope. Using the wrong keyword causes either an UnboundLocalError or silent modification of the wrong variable. This article explains the syntax, runtime behavior, and decision criteria for python global vs nonlocal.

The Scope Problem in Nested Functions

Python resolves names using the LEGB rule: Local, Enclosing, Global, Built-in. When you assign a value to a name inside a function, Python treats that name as local to the function unless you explicitly declare otherwise. This becomes a problem when you want to modify a variable from an outer scope inside a nested function.

Consider this minimal example:

def outer(): count = 0 def inner(): count += 1 inner() return count

Calling outer() raises UnboundLocalError: local variable 'count' referenced before assignment. The += operation reads count before writing, but because the assignment makes count local to inner, Python does not look into the enclosing scope. The name count is considered unbound within inner.

To fix this, you need a keyword that tells Python to target a different scope. That is where global and nonlocal come in.

What global Actually Does

The global statement declares that a name refers to a module-level variable. It works only for names that exist at the top level of a module. When you assign to a global name inside any function, the assignment updates the module-level binding.

counter = 0 def increment(): global counter counter += 1 increment() print(counter) # 1

Without global, the assignment counter += 1 would create a local counter and leave the module-level variable unchanged. global is necessary when a function must modify a variable that lives outside all functions.

global also works when the name is declared in a nested function, but it always refers to the module scope, not to any intermediate function scope. For example:

value = 10 def outer(): value = 20 def inner(): global value value = 30 inner() print(value) # 20 outer() print(value) # 30

Here global value inside inner bypasses the value defined in outer and modifies the module-level value. This behavior is often surprising and is a common source of bugs.

What nonlocal Actually Does

The nonlocal statement binds a name to a variable in the nearest enclosing function scope. It does not work at module level; it requires that the name already exists in an enclosing function. nonlocal is used inside nested functions to modify a variable that belongs to an outer function.

def outer(): count = 0 def inner(): nonlocal count count += 1 inner() return count print(outer()) # 1

nonlocal searches outward through enclosing function scopes until it finds the first binding. It does not affect module-level names. If no enclosing function defines the name, Python raises SyntaxError: no binding for nonlocal 'count' found.

Unlike global, nonlocal can target a variable that is several levels up:

def outer(): x = 1 def middle(): def inner(): nonlocal x x = 2 inner() middle() return x print(outer()) # 2

Here nonlocal x in inner finds x in outer, not in middle because middle does not define x. The search follows the enclosing scope chain.

Comparing global and nonlocal in the Same Program

To see both keywords in action, consider a function that uses a module-level constant and an enclosing counter:

base = 100 def make_adder(): total = 0 def add(amount): global base nonlocal total total += amount + base return add adder = make_adder() adder(5) print(adder.__closure__[0].cell_contents) # 105

global base allows the nested function to read and modify the module-level base. nonlocal total allows it to update the total variable that belongs to make_adder. The closure captures total, so the state persists between calls.

The following table summarizes the key differences:

Aspectglobalnonlocal
Scope targetedModule-levelNearest enclosing function
Required contextAny functionNested function only
Name must existNo, can createYes, must already exist in enclosing scope
Effect on outer functionNoneUpdates the variable in the enclosing function
Typical useModify module constants or shared stateModify state in a closure or decorator

Common Mistakes and Runtime Behavior

Forgetting nonlocal Causes UnboundLocalError

The most frequent mistake is omitting nonlocal when modifying an enclosing variable. As shown earlier, the assignment makes the name local, and Python raises UnboundLocalError when the code tries to read it before assignment.

Using global When You Mean nonlocal

If you use global inside a nested function to modify an outer function's variable, you will not get an error. Instead, you will silently modify a module-level variable, or create one if it does not exist. This can lead to hard-to-trace bugs because the outer function's variable remains unchanged.

Declaring nonlocal for a Name That Does Not Exist

nonlocal requires the name to be bound in an enclosing function. If you declare nonlocal x and no enclosing function defines x, Python raises a SyntaxError at compile time. This is different from global, which can create a new module-level name.

Interaction with Closures

nonlocal is essential for closures that maintain mutable state. Without it, you cannot reassign a variable captured from an outer function. For example, a simple counter closure:

def make_counter(): count = 0 def increment(): nonlocal count count += 1 return count return increment counter = make_counter() print(counter()) # 1 print(counter()) # 2

If you remove nonlocal, the count becomes local to increment and the closure loses its state.

When to Use Each: Decision Criteria

Choose global only when the variable truly belongs to the module level and you need to modify it from within a function. Common cases include configuration values, module-level counters, or shared state that must be visible across all functions in the module.

Choose nonlocal when you are inside a nested function and need to modify a variable from an enclosing function. This is typical in closures, decorators, and factory functions that maintain per-instance state.

Avoid using either keyword unless necessary. If you find yourself reaching for global frequently, consider whether the design can be refactored to pass state explicitly or use a class. nonlocal is more contained, but overusing it can make code harder to follow because the flow of data becomes implicit.

A practical rule: if the variable is used only within a single outer function and its nested helpers, nonlocal is appropriate. If the variable is shared across multiple independent functions, global might be justified, but a class or a module-level singleton often provides clearer ownership.

Maintainability and Readability Considerations

Both global and nonlocal introduce hidden dependencies. A reader must scan the entire scope chain to understand where a variable is defined and modified. This increases cognitive load and makes refactoring riskier.

To keep code maintainable, limit the number of variables modified through these keywords. Prefer returning updated values from functions when possible. For example, instead of:

def outer(): total = 0 def add(x): nonlocal total total += x add(5) add(10) return total

Consider:

def outer(): total = 0 def add(total, x): return total + x total = add(total, 5) total = add(total, 10) return total

The second version avoids nonlocal and makes the data flow explicit. This is not always possible, especially when the nested function is returned as a callback or used as a decorator, but it is worth considering when the nested function is only called internally.

When nonlocal is unavoidable, keep the enclosing function short and ensure the variable name clearly indicates its purpose. Document the side effect in a comment or docstring so future maintainers know the function modifies an outer state.

Edge Cases and Advanced Usage

nonlocal works with any immutable or mutable object, but it only rebinds the name. If the variable is a list or dictionary, you can modify its contents without nonlocal because you are not reassigning the name. For example:

def outer(): items = [] def add(item): items.append(item) # No nonlocal needed add(1) return items

This works because items.append does not assign to the name items. nonlocal is only required when you reassign the variable itself, such as items = items + [item].

Another edge case is using nonlocal in a class method that is nested inside another method. The enclosing scope is the method, not the class. If you need to modify a class attribute, you must use self or cls, not nonlocal.

Finally, both global and nonlocal are compile-time declarations. They affect the entire scope in which they appear, not just the block where they are written. This means you cannot conditionally declare a name as global or nonlocal based on runtime logic; the declaration applies to the whole function or nested function.

Understanding these edge cases helps you avoid subtle bugs and write Python code that behaves predictably across different nesting levels.

python global vs nonlocal: Practical Usage and Code Examples | RYUSLOG DEV