Python’s LEGB rule defines how variable name lookups occur across nested scopes. When Python encounters a variable name, it searches in order: Local (current function), Enclosing (outer functions in nested definitions), Global (module level), and Built-in (Python’s built-in namespace). Understanding this is crucial for avoiding subtle bugs, especially with closures, decorators, and nested functions.
Local scope includes variables defined or assigned within the current function. Enclosing scope matters in nested functions where inner functions can access outer function variables, creating closures. Global scope contains module-level variables and imports. Built-in scope includes Python’s built-in functions and exceptions like print(), len(), and ValueError.
Common pitfalls involve shadowing. If you assign to a variable name in a local scope, Python treats it as local throughout that function, even before assignment, causing UnboundLocalError if referenced before assignment. The ‘global’ keyword explicitly declares a variable should refer to global scope, while ‘nonlocal’ (Python 3+) refers to enclosing scope.
Closures capture variables from enclosing scope by reference, not value. This leads to classic bugs in loops where lambdas or nested functions capture loop variables, all referencing the same final value. List comprehensions have their own local scope (since Python 3), preventing variable leakage but sometimes surprising developers expecting different behavior.
In production code, scope confusion often appears in callback registrations, event handlers, configuration loading, and decorators. Understanding LEGB helps debug issues where variables have unexpected values, especially in complex applications with multiple module imports, nested function definitions, and dynamic configuration.