What is the LEGB rule in Python's scope resolution?
LEGB is Python's name resolution order: when the interpreter encounters a variable name, it searches scopes in this sequence: Local → Enclosing → Global → Built-in. The first scope where the name is found wins.
Local scope is the innermost function body currently executing. Variables assigned inside a function are local to that function by default — Python determines this at compile time, not runtime, so if a name is assigned anywhere in a function, it's treated as local throughout that function's scope (which causes the UnboundLocalError trap described below).
Enclosing scope covers any enclosing function scopes between the local function and the module level — the mechanism that makes closures work. A nested function can read variables from its enclosing function's scope. In Python 3, the `nonlocal` keyword allows a nested function to rebind (not just read) a variable in the enclosing scope.
Global scope is the module's top-level namespace. Variables defined at module level, or explicitly declared with the `global` keyword inside a function, live here. The `global` keyword tells Python to look up and bind the name in the module's global namespace instead of creating a local.
Built-in scope is the `builtins` module — containing `len`, `print`, `range`, `Exception`, etc. It's always available as the final fallback.
A common trap: the UnboundLocalError. If you read a variable before assigning it in the same function, Python has already classified it as local (because there's an assignment later in the function body) but hasn't assigned it yet at the point you read it. The fix is `global` or `nonlocal` depending on where the variable lives, or restructure to avoid the pattern.
Closures and LEGB interact importantly: a closure captures the enclosing scope's variable reference, not its value at the time of creation. The classic loop-closure bug (`lambda: i` in a for loop capturing the same `i`) stems from this. Default argument binding (`lambda i=i: i`) or `functools.partial` are the fixes.
Python's scoping is lexical (static) — determined by where code is written, not by the call stack at runtime.
Correctly names all four scopes in order, explains each with an example, and can describe what global and nonlocal do.
All of the above plus: explains the UnboundLocalError trap with the underlying cause (compile-time local classification), describes the closure variable capture semantic and the loop-lambda bug, and notes lexical vs dynamic scoping.
Reading the answer is step one. Explaining it unprompted — under interview pressure — is what actually matters. Get AI-graded feedback on your answer with follow-up probes on your weak points.
Get Graded — Free Assessment