Skip to content

Fixing Python UnboundLocalError When Modifying Global Variables

Python's scope rules can produce confusing errors when modifying global variables inside functions. This article explains the UnboundLocalError encountered in the provided StackOverflow question and presents best-practice solutions.

Problem Statement

Consider this scenario: You declare a global variable and try to modify it inside a function, but get this error:

python
UnboundLocalError: cannot access local variable 'a' where it is not associated with a value

Here's the problematic code:

python
a = 0  # Global variable

def test():
    a += 1  # Attempted modification
    print("The number is now", a)

The error occurs because:

  1. Python treats variables differently based on reading vs. modification
  2. You attempted to modify a global variable without explicitly declaring it as global
  3. Python interprets the inner a as a local variable that hasn't been initialized

Solution 1: Avoid Global Variables (Best Practice)

Global variables create hidden dependencies and make code harder to maintain. Instead, pass variables explicitly as arguments and return the updated value:

python
def increment(value):
    new_value = value + 1
    print("The number is now", new_value)
    return new_value

a = 0
a = increment(a)  # Explicitly pass and return values

Key advantages:

  • Eliminates hidden state dependencies
  • Makes function behavior predictable
  • Supports unit testing
  • Allows reuse in different contexts

Prefer Explicit Data Flow

When functions receive their dependencies as inputs and return updated states, your code becomes more readable and maintainable.

Solution 2: Using global Keyword (When Absolutely Necessary)

If you must modify a global variable, explicitly declare it inside the function:

python
a = 0

def test():
    global a  # Declare we're modifying the global
    a += 1
    print("The number is now", a)

Complete working application:

python
import keyboard
import time

a = 0

def test():
    global a
    a += 1
    print("The number is now", a)
    time.sleep(1)

while not keyboard.is_pressed('i'):
    test()

Use Global Variables Sparingly

Global state increases complexity and risk of bugs. Only use this approach when:

  • Dealing with application-wide configuration
  • Maintaining simple scripts where modularity isn't critical
  • Migrating legacy code

Why Other Approaches Fail

  1. nonlocal Doesn't Work:

    python
    # This will not work
    def test():
        nonlocal a  # ❌ nonlocal only works in nested functions
        a += 1

    nonlocal searches in enclosing scopes but doesn't look for globals, so its ineffective for simple module-level variables.

  2. Parameter Default Values Create Isolation:

    python
    a = 0
    
    def test(a=a):  # Captures initial value only
        a += 1      # Modifies local copy, not global
        print(a)
    
    test()  # Prints 1
    print(a)  # Still 0 - global unchanged

Deep Dive: Python Scope Rules

Key explanation points:

  • Reading: Python can access global variables without declaration
  • Modification: Assignment (=, +=, -= etc.) creates a local variable by default
  • Scope Chain: Python checks local → enclosing → global → built-in scopes during lookups
  • Declaration Keywords: Use global (module-level) or nonlocal (nested functions) to modify variables from outer scopes

When Global Might Be Acceptable

While generally discouraged, global variables can be appropriate in:

  • Configuration constants (read-only, uppercase naming)
  • Small scripts (< 50 lines)
  • State machines in embedded systems
  • Caching mechanisms (with thread safety)

Thread Safety Concern

When using global mutable state in multi-threaded applications, always implement locking mechanisms to prevent race conditions.

Summary of Recommendations

SituationApproachBest For
Modify shared statePass arguments + return valuesMost cases
Global configurationConstants in separate moduleApplication settings
Legacy code migrationglobal with migration planTemporary solutions
Nested functionsnonlocalClosures and decorators

Always prefer explicit data flow over implicit state changes. When global state is unavoidable:

  1. Isolate global variables in a dedicated module
  2. Use descriptive names (avoid single-letter variables)
  3. Document all access points clearly
  4. Consider thread safety in concurrent applications

For simple scripts where global seems necessary, ensure you:

  • Declare global variable_name before any use
  • Avoid recursion that might reset the global state
  • Never use the same name for global and local variables