Desk Clarity

Everyday document & browser help

Python Loop Lambdas Return the Last Value: Capture Each Callback’s Input

By ON Lab · Sources checked November 9, 2026

Inspect when the callback reads its variable. A default argument can capture the current value while the function is created; a closure otherwise reads the binding when called.

Reproduce the boundary in a disposable example

Callbacks created in a loop return the same value when called later.

callbacks = [lambda: n * 10 for n in range(3)]
assert [f() for f in callbacks] == [20, 20, 20]
captured = [lambda n=n: n * 10 for n in range(3)]
assert [f() for f in captured] == [0, 10, 20]
# Capturing a mutable object captures its reference, not a snapshot.
record = {"amount": 1}
f = lambda record=record: record["amount"]
record["amount"] = 9
assert f() == 9
print("late binding and reference capture verified")

Read the result before changing application code

The first assertion separates callback creation from callback execution. Calling each lambda immediately inside the loop would hide the delayed-read problem. The last assertion is a second boundary: storing the current dictionary reference does not freeze its contents.

Choose the behavior your caller needs

For numeric identifiers, a captured default is often enough. For an event API that supplies an argument, keep the event parameter separate, for example lambda event, n=n: handle(n). Otherwise the event might replace the captured value. A small factory function is easier to read when callbacks need several captured values. Decide whether later edits to a captured object should be visible. Copy only the data whose snapshot semantics you actually need.

Scope and limits

This covers Python closure binding, not callback thread safety or an event framework’s argument contract. Examples are original local checks; official FAQ supplies the language rule.

Related: compare the adjacent data or execution boundary.

The embedded example was executed in an isolated local Python process on November 9, 2026. It is a reproducible boundary check, not a test of your full application.

Sources