The reason is that, at creation time, the generator (a for b in c if d)
only evaluates c
(which sometimes makes b
predictable as well). But a
, b
, d
are evaluated at consumption time (at each iteration). Here, it uses the current binding of array
from the enclosing scope when evaluating d
(array.count(x) > 0
).
You can for instance do:
g = (x for x in [] if a)
Without having declared a
in advance. But, you have to make sure a
exists when the generator is consumed.
But you cannot do similarly:
g = (x for x in a if True)
Upon request:
You can observe similar (however not identical) patterns with a common generator function:
def yielder():
for x in array:
if array.count(x) > 0:
yield x
array = [1, 8, 15]
y = yielder()
array = [2, 8, 22]
list(y)
# [2, 8, 22]
The generator function does not execute any of its body ahead of consumption. Hence, even the array
in the for-loop header is bound late. An even more disturbing example occurs where we "switch out" array
during iteration:
array = [1, 8, 15]
y = yielder()
next(y)
# 1
array = [3, 7]
next(y) # still iterating [1, 8, 15], but evaluating condition on [3, 7]
# StopIteration raised
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…