Python Tutorial
Python Global Variables
Variables created outside a function are global. Use the global keyword to change them from inside a function.
Global Scope
A variable created outside a function can be used inside it.
x = "awesome"
def myfunc():
print("Python is " + x)
myfunc()Local Shadows Global
A variable with the same name inside a function is local and does not change the global.
x = "awesome"
def myfunc():
x = "fantastic"
print("Python is " + x)
myfunc()
print("Python is " + x)The global Keyword
Tell Python you mean the global variable, then you can assign to it inside the function.
x = "awesome"
def myfunc():
global x
x = "fantastic"
myfunc()
print("Python is " + x)📘 Real-World Deep Dive
Module-level globals are convenient but a frequent source of test pain and hidden coupling. Knowing when to use them, and how to scope them with <code>global</code>, keeps your code testable.
Real-Life Scenario
A small request logger that increments a thread-safe counter, writes each entry to a global event log, and exposes a snapshot helper for dashboards.
Real-Life Example
import csv, threading
from datetime import datetime, timezone
# Module-level globals — kept tight and named in CAPS for visibility.
COUNTER_LOCK = threading.Lock()
REQUEST_COUNT = 0
EVENT_LOG: list[str] = []
def _increment() -> None:
global REQUEST_COUNT
with COUNTER_LOCK:
REQUEST_COUNT += 1
def log_request(method: str, path: str, status: int) -> None:
_increment()
EVENT_LOG.append(f"{datetime.now(timezone.utc).isoformat()} {method} {path} {status}")
def snapshot() -> dict[str, object]:
with COUNTER_LOCK:
return {"count": REQUEST_COUNT, "events": list(EVENT_LOG)}
for m, p, s in [("GET", "/", 200), ("POST", "/login", 200),
("GET", "/missing", 404), ("POST", "/login", 401)]:
log_request(m, p, s)
import json
print(json.dumps(snapshot(), indent=2))Expected Output
{
"count": 4,
"events": [
"2026-08-20T22:01:40.123456+00:00 GET / 200",
"..."
]
}Common mistakes
- Mutable global lists / dicts are invisible to the type-checker; treat them as configuration, not state.
- Calling
globalisn't required for reading; only for rebinding. - Side-effects in module-level statements fire on import — keep them cheap.
🚀 Performance & Best Practices
- Lock-free counters (
itertools.count) are faster thanwith LOCK. - Cache-bounded global lists with
collections.deque(maxlen=N). - Reset globals explicitly in test fixtures; don't rely on import-order mutations.
🧪 Try It Yourself
- Wrap
EVENT_LOGincollections.deque(maxlen=10_000). - Refactor
log_requestto take a class-based logger instead. - Add an audit hook that records the calling frame when a global is mutated.