DEV Community

Cover image for Python Scope Is Simpler Than You Think. Here Is the One Rule That Explains Everything.
Ameer Abdullah
Ameer Abdullah

Posted on

Python Scope Is Simpler Than You Think. Here Is the One Rule That Explains Everything.

Every Python scope question in every interview boils down to one rule.

When Python looks up a name, it searches in this order:

Local → Enclosing → Global → Built-in

That is the LEGB rule. If you understand it completely, no scope question will ever surprise you.

Let me show you how it applies to the five scope situations that actually appear in interviews.


Situation 1: The Basic Lookup

x = "global"

def show():
    print(x)

show()
Enter fullscreen mode Exit fullscreen mode

Output: global

The function has no local x. Python moves outward and finds x in the global scope.


Situation 2: Local Shadows Global

x = "global"

def show():
    x = "local"
    print(x)

show()
print(x)
Enter fullscreen mode Exit fullscreen mode

Output:

local
global
Enter fullscreen mode Exit fullscreen mode

The assignment inside show creates a new local x. It does not modify the global x. After the function returns, the global x is unchanged.


Situation 3: The UnboundLocalError Trap

x = 10

def increment():
    x += 1
    print(x)

increment()
Enter fullscreen mode Exit fullscreen mode

Output: UnboundLocalError: local variable 'x' referenced before assignment

This surprises most people. The function reads x on the right side of += before assigning to it. But because there is an assignment to x inside the function (the +=), Python marks x as local for the entire function scope before execution begins. Reading a local variable before assigning to it raises UnboundLocalError.

Fix with global x inside the function if you truly need to modify the global.


Situation 4: Enclosing Scope (Closures)

def outer():
    count = 0
    def inner():
        nonlocal count
        count += 1
        return count
    return inner

counter = outer()
print(counter())
print(counter())
print(counter())
Enter fullscreen mode Exit fullscreen mode

Output:

1
2
3
Enter fullscreen mode Exit fullscreen mode

inner accesses count from the enclosing scope of outer. Without nonlocal, assigning to count inside inner would trigger the same UnboundLocalError as situation 3. With nonlocal, the enclosing scope variable is modified directly.


Situation 5: The Late Binding Closure

functions = []
for i in range(3):
    functions.append(lambda: i)

print([f() for f in functions])
Enter fullscreen mode Exit fullscreen mode

Output: [2, 2, 2]

Most people expect [0, 1, 2].

The lambdas do not capture the value of i at the time they are created. They capture a reference to the variable i in the enclosing scope. By the time the lambdas are called, the loop has finished and i is 2.

Fix: lambda i=i: i captures the current value as a default argument.


The One-Sentence Summary

Python looks up names outward from the current scope and always finds the first match. Assignment creates a new name in the current scope unless you declare otherwise with global or nonlocal.

Every scope question is a variation of this rule. Once you see it clearly, the variations are just details.

Practice predicting scope behavior on PyCodeIt, the interview prep mode has scope-specific problems that test exactly these patterns.


Top comments (0)