Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
244 views
in Technique[技术] by (71.8m points)

python - Generator expression uses list assigned after the generator's creation

I found this example and I can't understand why it works unpredictably? I supposed it must output [1, 8, 15] or [2, 8, 22].

array = [1, 8, 15]
g = (x for x in array if array.count(x) > 0)
array = [2, 8, 22]
print(list(g))


>>>[8]
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...