Python Practice Question 10: Late Binding Closures
Python · Test 1 · question 10 of 10
On CPython 3.12, what does this code print? fs = [lambda: i for i in range(3)]; print([f() for f in fs])
Show answer and explanation
Answer:
A lambda (or nested def) does not capture the *value* a variable had when it was created. It refers to the variable itself and looks up its value when it is called, late binding. All three lambdas refer to the same i, the loop variable of the comprehension. By the time [f() for f in fs] calls them, the loop has finished and i is bound to its final value, 2, so each call returns 2.
Option A is what you get if you bind the current value at creation time, most commonly with a default argument: [lambda i=i: i for i in range(3)] gives [0, 1, 2], because defaults are evaluated when the lambda is defined. functools.partial is the other common fix.
Option C mixes up two true facts. In Python 3, a comprehension's variable is isolated from the enclosing scope: print(i) right after the comprehension raises NameError. That stays true in 3.12 even though PEP 709 inlines comprehensions. But closures created inside the comprehension keep a reference to that variable's cell, so they can still read it after the comprehension ends.
Option D assumes the loop variable goes one past the end. range(3) produces 0, 1, 2, and the variable's last bound value is 2; no 3 is ever assigned.
Reference: Programming FAQ: Why do lambdas defined in a loop with different values all return the same result?