Global vs. local scope determines where a variable can be accessed (read) and modified in Python. Understanding scope prevents bugs like NameError, unexpected side effects, and hard-to-debug behavior — especially in larger codebases or when using functions heavily.
In 2026, modern Python code minimizes global variables, favors local scope, and uses explicit mechanisms (global, nonlocal, function returns) when crossing boundaries. Here’s a complete, practical guide to scope rules, with real examples and best practices.
1. Local Scope (Inside Functions)
Variables defined inside a function are local — they exist only while the function is running and are not visible outside.
def my_function():
x: int = 10 # local variable
print(f"Inside function: x = {x}")
my_function() # Inside function: x = 10
# print(x) # NameError: name 'x' is not defined
2. Global Scope (Outside Functions)
Variables defined at the module level (top of the file) are global — accessible (readable) from anywhere, including inside functions.
global_var: int = 100 # global variable
def read_global():
print(f"Inside function: global_var = {global_var}")
read_global() # Inside function: global_var = 100
print(global_var) # 100 (still accessible outside)
3. Modifying Globals Inside Functions (Use global)
Reading a global is fine, but modifying it requires the global keyword — otherwise Python creates a new local variable.
count: int = 0
def increment():
# count += 1 # UnboundLocalError: local variable 'count' referenced before assignment
global count
count += 1
print(f"Inside: count = {count}")
increment() # Inside: count = 1
print(f"Outside: count = {count}") # Outside: count = 1
4. Nested Functions & nonlocal
In nested functions, nonlocal lets you modify variables from the enclosing scope (not global, not local).
def outer():
message: str = "Hello"
def inner():
# message += " world" # UnboundLocalError
nonlocal message
message += " world"
print(f"Inner: {message}")
inner()
print(f"Outer: {message}")
outer()
# Inner: Hello world
# Outer: Hello world
5. Scope Resolution Order (LEGB Rule)
Python looks for variables in this order: - **L**ocal (inside current function) - **E**nclosing (nonlocal scopes in nested functions) - **G**lobal (module level) - **B**uilt-in (e.g.,print, len)
x: int = 1 # global
def outer():
x: int = 2 # enclosing (nonlocal)
def inner():
x: int = 3 # local
print(x) # 3 (local wins)
inner()
print(x) # 2 (enclosing)
outer()
print(x) # 1 (global)
Best Practices & Common Pitfalls (2026 Edition)
- Avoid globals — pass values as parameters and return results — makes code testable and predictable
- Use type hints —
x: int— clarifies intent and catches scope errors early with mypy - Prefer return over globals — functions should be pure when possible (no side effects)
- Pitfall — modifying globals without
global? creates local shadow instead - Pitfall — mutable globals (lists, dicts) — dangerous shared state ? prefer passing copies
- Modern tip — use
@dataclassorTypedDictinstead of global dicts for configuration - Production — never rely on globals for important state — use dependency injection or config objects
Conclusion
Scope (global vs. local) controls variable visibility and lifetime. In 2026, minimize globals, use global/nonlocal only when necessary, prefer parameters and returns, and leverage type hints for clarity. Understanding LEGB and avoiding mutable globals prevents most scope-related bugs. Write functions that are self-contained and explicit — your code becomes easier to read, test, and scale.
Next time you see a variable used inside a function — ask: “Is it local, nonlocal, global, or built-in?” Knowing the answer saves hours of debugging.