Andrew Barnert via Python-ideas writes:
> powers = [lambda x: x**i for i in range(10)]
> This gives you ten functions that all return x**9, which is
> probably not what you wanted.
> The reason this is a problem is that Python uses "late binding",
> which in this context means that each of those functions is a
> closure that captures the variable i in a way that looks up the
> value of i at call time. All ten functions capture the same
> variable, and when you later call them, that variable's value is
> 9.
But this explanation going to confuse people who understand the
concept of variable in Python to mean names that are bound and
re-bound to objects. The comprehension's binding of i disappears
before any element of powers can be called. So from their point of
view, either that expression is an error, or powers[i] closes over a
new binding of the name "i", specific to "the lambda's scope" (see
below), to the current value of i in the comprehension.
Of course the same phenomenon is observable with other scopes. In
particular global scope behaves this way, as importing this file
i = 0
def f(x):
return x + i
i = 1
and calling f(0) will demonstrate. But changing the value of a
global, used the way i is here, within a library module is a rather
unusual thing to do; I doubt people will observe it.
Also, once again the semantics of lambda (specifically, that unlike
def it doesn't create a scope)
seem to be a source of confusion more
than anything else. Maybe it's possible to exhibit the same issue
with def, but the def equivalent to the above lambda
>>> def make_increment(i):
... def _(x):
... return x + i
... return _
...
>>> funcs = [make_increment(j) for j in range(3)]
>>> [f(0) for f in funcs]
[0, 1, 2]
closes over i in the expected way. (Of course in practicality, it's
way more verbose, and in purity, it's not truly equivalent since
there's at least one extra nesting of scope involved.)
While
>>> def make_increment():
... def _(x):
... return x + i
... return _
...
>>> funcs = [make_increment() for i in range(3)]
>>> [f(0) for f in funcs]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <listcomp>
File "<stdin>", line 3, in _
NameError: name 'i' is not defined
>>> i = 6
>>> [f(0) for f in funcs]
[6, 6, 6]
doesn't make closures at all, but rather retains the global binding.