Functions
Variable Scope
Local, global, nonlocal scope and the LEGB rule
Interview: Common interview topic — LEGB rule and closure scoping are frequently tested
Variable scope determines where a variable can be accessed and modified. Python uses the LEGB rule (Local, Enclosing, Global, Built-in) to resolve variable names. Understanding scope is critical for avoiding subtle bugs, especially with nested functions, closures, and class methods.
The LEGB Rule
When Python encounters a variable name, it searches in this order:
- L — Local: Variables defined inside the current function
- E — Enclosing: Variables in any enclosing function (for nested functions)
- G — Global: Variables defined at the module level (top level of the file)
- B — Built-in: Predefined names like
len,print,range
The global Keyword
- Reading globals: You can READ global variables without the
globalkeyword - Writing globals: You MUST declare
global xto MODIFY a global variable - Anti-pattern: Using global variables is generally discouraged — prefer function parameters and return values
The nonlocal Keyword
- Enclosing scope:
nonlocalallows an inner function to modify a variable from its enclosing function - Closures: Essential for implementing closures that maintain state between calls
- Search upward: nonlocal searches enclosing scopes from innermost to outermost (not global)
- Error:
nonlocalraises SyntaxError if the variable doesn't exist in any enclosing scope
Scope Bug: Late Binding in Loops
A classic Python trap: funcs = [lambda: i for i in range(3)] — all lambdas return 2! The variable i is looked up at call time, not definition time. Fix: lambda i=i: i to capture the current value.
Pro Tip: Avoid Global State
Instead of global variables, use classes with instance variables or closures with nonlocal. This makes code testable, thread-safe, and avoids hidden dependencies between functions.
Use Cases
Implementing closures that maintain state between function calls
Understanding and debugging variable resolution in nested functions
Designing factory functions that capture configuration in their scope
Avoiding the late-binding trap in loop-generated callbacks
Writing decorators that need to track state across invocations
Common Mistakes
Late binding in loops — lambdas/comprehensions capture variable names, not values
UnboundLocalError — assigning to a variable in a function makes it local for the ENTIRE function
Forgetting that reading a global doesn't need "global" but writing does
Using mutable class variables instead of instance variables — shared across all instances
Overusing global variables instead of passing parameters — makes code hard to test and reason about