# Python namespace **Python namespace** is a mapping from names to objects. Every scope has its own namespace (global, local, built-in), and Python searches them in order using LEGB (Local, Enclosing, Global, Built-in) to find what a name refers to. Understanding scopes prevents variable shadowing bugs and clarifies how closure variables work. Use `locals()` and `globals()` to inspect namespaces, and understand scope rules to debug mysterious variable references. ## Example This example demonstrates namespace scope and variable shadowing. ```python # run: python3 namespace.py # description: namespace scope and LEGB resolution x = "global" def outer(): x = "enclosing" def inner(): x = "local" print(f"Inner: {x}") print(f"Inner locals: {list(locals().keys())}") inner() print(f"Outer: {x}") outer() print(f"Global: {x}") # Closure and nonlocal def counter(): count = 0 # enclosing scope def increment(): nonlocal count # access enclosing scope count += 1 return count def get(): return count return increment, get inc, get = counter() print(f"\nCounter: {inc()}, {inc()}, {inc()}") print(f"Current: {get()}") # globals() and locals() print(f"\nGlobal 'x': {globals()['x']}") def show_scopes(): y = "local var" print(f"Local namespace keys: {list(locals().keys())}") print(f"y in locals: {'y' in locals()}") print(f"x in globals: {'x' in globals()}") show_scopes() ``` ## Common patterns **LEGB lookup order**: - **L**: Local (function scope) - **E**: Enclosing (outer function scope for nested functions) - **G**: Global (module scope) - **B**: Built-in (built-in functions like `len`, `print`) - Python searches in this order; first match wins **Scope keywords**: - `global var`: access/modify global scope from function - `nonlocal var`: access/modify enclosing scope from inner function - Without these, assignment creates a new local variable **Closures**: - Inner function captures enclosing scope variables - Variables must exist at function definition time - Useful for factories, decorators, callbacks **Inspecting namespaces**: - `locals()`: dict of local namespace - `globals()`: dict of global namespace - `vars(obj)`: object's attribute namespace (equivalent to `obj.__dict__`) - `dir(obj)`: list of accessible attributes **Common mistakes**: - Forgetting `nonlocal` and thinking you're modifying enclosing variable (creates local instead) - Variable shadowing: local `x` hides global `x` invisibly - Late binding in closures: inner function captures variable name, not value - Mutable defaults: `def f(x=[]):` modifies same list on each call **Late binding example** (common gotcha): ```python funcs = [] for i in range(3): def f(): return i # captures 'i' by reference, not value funcs.append(f) print([f() for f in funcs]) # [2, 2, 2] not [0, 1, 2]! # Fix: capture value with default arg: def f(i=i): ```