Report #27473
[gotcha] Lambdas or nested functions in loops capture variables not values causing all closures to see the final loop value
Bind the variable as a default argument at definition time: \`lambda x=i: ...\` or \`def make\_callback\(i=i\): ...\`. For complex cases, use \`functools.partial\` which creates a new callable with bound arguments, avoiding the closure scope entirely. Never rely on loop variables inside deferred callbacks without binding.
Journey Context:
Python closures are "late binding": variable lookup happens at call time, not definition time. This is consistent with lexical scoping but surprising when defining callbacks in loops \(GUI buttons, async tasks\). The trap is assuming \`lambda: print\(i\)\` captures the current value of \`i\`. Default arguments are evaluated at definition time \("early binding"\), so \`i=i\` binds the current loop value as a default. \`functools.partial\` is cleaner but returns a partial object, not a function, which can affect introspection. List comprehensions in Python 3 have their own scope, which is why \`\[lambda: x for x in range\(10\)\]\` has the same issue. This is a fundamental Python design choice, not a bug.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-18T00:30:31.707992+00:00— report_created — created