AtomLearnAtomLearn
DashboardGoalsPathAchievementsReviewSign In
DashboardPython CoreClosures & Variable Capture
Python CoreNot Started

Closures & Variable Capture

How closures capture variables, the loop lambda gotcha, nonlocal, and UnboundLocalError

0%
Knowledge0%
Learn & Drill
Fluency0%
Drill & Speed
Retention0%
Mastery & Review
Confidence0%
All modes
Practice

Free tier: read the explanation here. Upgrade to Pro for Drills, Speed challenges & Mastery badges.

Upgrade
Knowledge
Fluency
Retention

Knowledge Debt detected

You can study this freely — but your score may plateau if these foundations have gaps. The Mastery badge requires them to be solid.

Explanation

A is a function that remembers the variables from its even after that scope has finished.

Basic closure:

python def make_counter(start=0): count = start def increment(): nonlocal count # needed to modify enclosing variable count += 1 return count return increment counter = make_counter() counter() # 1 counter() # 2

The UnboundLocalError trap:

python x = 10 def f(): print(x) # UnboundLocalError! Python sees x = x + 1 below x = x + 1 # and decides x is local throughout the function `` Python decides at whether a variable is local or not. If it's assigned anywhere in the function, it's considered local everywhere in that function.

The loop lambda gotcha (a classic interview trap): ``python funcs = [lambda: i for i in range(3)] print([f() for f in funcs]) # [2, 2, 2] — NOT [0, 1, 2] All lambdas capture the same i variable :by-reference[The closure stores a pointer to the variable itself, not a copy of its value — so if the variable changes later, the closure sees the new value] not by value. By the time they run, i` is 2.

Fix — capture by value using default argument:

python funcs = [lambda i=i: i for i in range(3)] print([f() for f in funcs]) # [0, 1, 2] ✓ The i=i creates a new local variable i` for each lambda, bound to the current loop value.

Examples

Closure as a configurable function factory

Each call to make_multiplier creates a new closure with its own enclosing scope

def make_multiplier(factor):
    def multiply(x):
        return x * factor   # captures 'factor' from enclosing scope
    return multiply

double = make_multiplier(2)
triple = make_multiplier(3)

print(double(5))   # 10
print(triple(5))   # 15
# Each closure has its OWN copy of 'factor'

How well did you understand this?

Next in Python Core

Lambdas

Continue