发电机可以多次使用吗?

16

这是我的代码片段,其中定义了两个生成器:

one_line_gen = (x for x in range(3))

def three_line_gen():
    yield 0
    yield 1
    yield 2

当我执行:

for x in one_line_gen:
    print x

for x in one_line_gen:
    print x

结果如预期:

0
1
2

然而,如果我执行:

for x in three_line_gen():
    print x

for x in three_line_gen():
    print x

结果是:

0
1
2
0
1
2

为什么?我以为所有的发电机只能用一次。


2
不要将生成器函数与其产生的生成器混淆。 - Martijn Pieters
换句话说,如果你使用了 def one_line_gen(): return (x for x in range(3)),你的第一个例子将是等效的(每次调用函数都会产生一个生成器)。 - Martijn Pieters
5个回答

16

three_line_gen不是一个生成器,它是一个函数。当你调用它时返回的是一个生成器,每次你调用它都是一个全新的生成器。每次你像这样使用括号:

three_line_gen不是一个生成器,它是一个函数。当你调用它时返回的是一个生成器,每次你调用它都是一个全新的生成器。每次你像这样使用括号:

three_line_gen()

这是一个全新的生成器,需要进行迭代。但是,如果您首先执行

mygen = three_line_gen()

如果对 mygen 进行两次迭代,第二次迭代会按照你的预期失败。

Translated:

If you iterate over mygen twice, the second time will fail as you expect.


8

不,你不能对生成器进行两次迭代。一旦你对生成器进行迭代,它就会被耗尽。但是你可以使用tee复制一个生成器:

from itertools import tee

one_line_gen = (x for x in range(3))
gen1, gen2 = tee(one_line_gen)
# or: 
# gen1, gen2 = tee(x for x in range(3))

for item in gen1:
    print(item)

for item in gen2:
    print(item)

其他问题请参见Ofer Sadan的回答


2
因为每次调用three_line_gen()都会创建一个新的生成器。
否则,你说的没错,生成器只能一直向前运行直到耗尽。
是的,如果结果在生成器外被缓冲,那么生成器可以被多次使用。其中一种简单的方法是使用itertools.tee()
>>> from itertools import tee
>>> def three_line_gen():
        yield 0
        yield 1
        yield 2

>>> t1, t2 = tee(three_line_gen())
>>> next(t1)
0
>>> next(t2)
0
>>> list(t1)
[1, 2]
>>> list(t2)
[1, 2]

1

是的,生成器只能使用一次。但是您有两个生成器对象。

# Python 3


def three_line_gen():
    yield 0
    yield 1
    yield 2

iterator = three_line_gen()
print(iterator)
for x in iterator:
    print(id(iterator), x)

iterator2 = three_line_gen()
print(iterator2)
for x in iterator2:
    print(id(iterator2), x)

结果是:

<generator object three_line_gen at 0x1020401b0>
4328784304 0
4328784304 1
4328784304 2
<generator object three_line_gen at 0x1020401f8>
4328784376 0
4328784376 1
4328784376 2

0

因为一行代码是一个生成器对象,而三行代码是一个函数。它们的意义不同。

这两个看起来相似,但实际上有所不同。

def three_line_gen_fun():
    yield 0
    yield 1
    yield 2

three_line_gen = three_line_gen_fun()
one_line_gen = (x for x in range(3))

type(three_line_gen) == type(one_line_gen)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接