21.3 global and nonlocal Declarations
In Python, variable resolution follows the LEGB rule: Local, Enclosing, Global, Built-in. This systematic search order ensures predictable behavior, but sometimes we need to explicitly instruct the interpreter to modify a variable from a non-local scope rather than create a new local one. This is where the global and nonlocal declarations become essential. They are explicit statements that break the default read-only relationship with variables in outer scopes, allowing assignment operations to affect those specific namespaces directly.
The global Declaration
The global keyword is used to declare that a variable inside a function’s local scope refers to a name in the module’s global scope. Without this declaration, assigning a value to a name inside a function creates a new local variable, even if a global variable of the same name exists. The global statement tells the Python interpreter to bind the name’s assignment and subsequent lookups to the global namespace.
count = 0 # This is a global variable
def increment_counter():
global count # Declare 'count' as global
count += 1 # Now this modifies the global 'count'
print(f"Inside function: {count}")
increment_counter()
print(f"Outside function: {count}") # Output: Outside function: 1
Contrast this with the behavior without the global declaration, which demonstrates a common pitfall:
count = 0
def try_increment():
# Without 'global', this next line is interpreted as creating a new local variable.
# Since 'count' is referenced on the right-hand side before assignment, it causes an error.
count = count + 1 # UnboundLocalError: local variable 'count' referenced before assignment
try_increment()
The global declaration is required for both modifying mutable objects in-place and rebinding the name itself. However, for mutable objects like lists, you can modify their contents without a global declaration because you are not reassigning the name; you are operating on the object it references.
my_list = [1, 2, 3] # Global list
def append_to_list():
my_list.append(4) # This works! Modifying the object, not rebinding the name.
# my_list = [5, 6] # This would require 'global' as it rebinds the name.
append_to_list()
print(my_list) # Output: [1, 2, 3, 4]
The nonlocal Declaration
The nonlocal keyword is used in nested functions (closures) to declare that a variable refers to a name in the nearest enclosing scope that is not global. It allows an inner function to modify a variable from an outer (but non-global) scope. This is crucial for creating closures that maintain and update state.
def outer_function():
value = "Original" # Enclosing (non-global) scope variable
def inner_function():
nonlocal value # Declare 'value' as nonlocal
value = "Modified" # This modifies the enclosing scope's 'value'
inner_function()
print(value) # Output: Modified
outer_function()
Without nonlocal, the inner function would create a new local variable named value, leaving the enclosing scope’s variable unchanged—a subtle and often confusing bug.
def outer_function():
value = "Original"
def inner_function():
value = "New Local" # This creates a new local variable, shadowing the outer one.
inner_function()
print(value) # Output: Original (The outer variable was not changed)
outer_function()
Key Differences and Best Practices
It is critical to understand the distinction between global and nonlocal. global always references the top-most module-level scope. nonlocal references the nearest enclosing scope, which could be several levels deep in a nested function chain. A nonlocal variable must already exist in an enclosing scope; you cannot create a new one with nonlocal.
x = "global"
def outer():
x = "nonlocal"
def inner():
nonlocal x # Refers to the 'x' in outer()
x = "changed nonlocal"
# global x # This would refer to the module-level 'x', not outer()'s.
# x = "changed global"
inner()
print(x) # Output: changed nonlocal
outer()
print(x) # Output: global (the module-level variable remains unchanged)
Common Pitfalls and Best Practices:
- Clarity Over Cleverness: Overusing
globalcan lead to code that is difficult to debug and reason about, as any part of the program can change the variable’s state. It often indicates a design that might be better served by passing arguments and returning values or using class attributes to manage state. nonlocalfor Stateful Closures: The primary legitimate use case fornonlocalis in closures that need to remember and update state between calls, such as decorators or function factories.- Declaration Before Use: Both
globalandnonlocalmust be declared before the name is used in the scope. They are directives to the compiler about how to handle the name throughout the entire code block. - No New
nonlocalVariables: You cannot usenonlocalto create a new variable in an enclosing scope; the name must already exist there. Attempting to do so results in aSyntaxError.