ReviseAlgo Logo

Functions

Variable Scope

Local, global, nonlocal scope and the LEGB rule

Interview: Common interview topic — LEGB rule and closure scoping are frequently tested

Last Updated: June 12, 2026 10 min read

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 global keyword
  • Writing globals: You MUST declare global x to MODIFY a global variable
  • Anti-pattern: Using global variables is generally discouraged — prefer function parameters and return values

The nonlocal Keyword

  • Enclosing scope: nonlocal allows 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: nonlocal raises 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