Python Global Variables: Scope, Syntax, and Pitfalls
python global variable: Learn how Python global variables work: the global keyword, LEGB scope resolution, mutable object traps, thread safety, and when to avoid them.
Understanding how a python global variable behaves requires knowing how Python resolves names across scopes. Python uses the LEGB rule: Local, Enclosing, Global, Built-in. When a function references a name, the interpreter checks the local scope first, then any enclosing function scopes, then the module-level (global) scope, and finally the built-in scope.
How Python Resolves Variable Names
Consider this function that tries to increment a module-level counter:
counter = 0 def increment(): counter += 1
Running increment() raises UnboundLocalError: local variable 'counter' referenced before assignment. The cause is subtle. Python determines variable scope at compile time, not at runtime. Because counter is assigned anywhere inside the function body, Python treats it as a local variable for the entire function. The += operator reads counter before writing it, and since the local binding does not exist yet, the error fires.
Reading a Global Variable Without the global Keyword
Reading a global variable inside a function requires no special syntax:
threshold = 10 def is_above_threshold(value): return value > threshold
The name threshold is only read, never assigned, so Python resolves it through the global scope. This is the most common legitimate use of global variables: read-only configuration values that functions consult but do not modify.
The global Keyword for Assignment
To rebind a global variable from inside a function, you must declare it with global:
attempts = 0 def record_attempt(): global attempts attempts += 1
The global statement tells the interpreter that attempts refers to the module-level binding rather than a new local variable. Without it, the function would silently create a local attempts and leave the module-level value untouched.
Why global Must Appear Before Any Use
The global declaration must appear before the variable is used in the function body. Python scans the entire function to determine which names are local, and global overrides that determination for the names it lists.
value = 5 def broken(): print(value) # UnboundLocalError value = 10 def fixed(): global value print(value) # 5 value = 10
In broken, the assignment at the end makes value local for the whole function, so the print call fails. In fixed, the global declaration resolves the name to the module scope for both the read and the write.
Mutable Global Objects: A Common Trap
A mutable object such as a list or dictionary can be modified without global because mutation does not rebind the name:
cache = {} def add_to_cache(key, value): cache[key] = value # No global needed
This works because cache[key] = value invokes the dict's __setitem__ method; the name cache itself is only read. Reassigning the name, however, requires global:
def reset_cache(): global cache cache = {}
The distinction between mutating an object and rebinding a name is the most frequent source of confusion with global variables in Python.
Thread Safety and Shared Mutable State
Global variables are shared across all threads in a process. If multiple threads read and write the same global, you need explicit synchronization:
import threading counter = 0 lock = threading.Lock() def increment(): global counter with lock: counter += 1
Even counter += 1 is not atomic. The interpreter reads the value, adds one, and writes it back. Two threads can interleave those operations and lose an increment. The lock serializes access to the counter. This is a concrete production concern, not a theoretical one.
Module-Level State as a Practical Pattern
The most maintainable use of a global variable is module-level state that is initialized once and read by multiple functions. A configuration module is a typical example:
# config.py settings = { "timeout": 30, "retries": 3, } def get_timeout(): return settings["timeout"]
Other modules import settings and read from it. Because the dict is mutable, individual keys can be updated without global in the importing module. But if a function reassigns settings itself, that function needs its own global declaration.
When to Avoid Global Variables
Global variables make testing harder because each test must reset shared state. They also create hidden coupling: any function that reads a global depends on whatever code last wrote to it. For small scripts this is acceptable. For larger applications, prefer passing values as parameters or holding state in a class instance.
The practical decision rule: use a global when the state is truly process-wide and read-mostly, such as a configuration value loaded at startup. Avoid globals when the state changes frequently or when different callers need different values. In those cases, explicit parameter passing makes the data flow visible and testable.