4 ms·
I'm being a bit pedantic, but the last snippet under About lambda and closures-in-a-loop pitfall is arguably misleading when it says, "This, however, does not a
by andolanra 13y ago
I'm being a bit pedantic, but the last snippet under About lambda and closures-in-a-loop pitfall is arguably misleading when it says, "This, however, does not apply to generators…" Which is true in the example given, but not true in general. Generators don't actually provide a new environment on each evaluation, they just lazily construct their contents, which assuages the problem in certain situations. This snippet
for l in (lambda: n for n in range(3)):
print(l()) # prints 0, 1, 2
will alternately request an argument from the generator, return a lambda, call it, and only then request the next argument from the generator, which means at the time of the funtion call, n will not have been incremented up to its final value. We can see the previous closures-in-a-loop behavior return if we keep the closures around to execute again, at which point the loop environment contains the final value of n:
funcs = []
for x in (lambda: n for n in range(3)):
funcs.append(x)
print(x()) # prints 0, 1, 2
for y in funcs:
print(y()) # prints 2, 2, 2
So generators don't 'fix' the problem by constructing distinct environments for each loop iteration, but by deferring evaluation of their body until necessary.
- MereInterest 13y agoYeah, that part stood out to me as well. The only way I've found to get different values for each of the functions is to use a different closure for each functions. funcs = [(lambda i: (lambda :i))(n) for n in range(3)] for f in funcs: print(f())