为什么Python的itertools.cycle需要创建iterable的副本?

时间:2013-05-19 19:33:53

标签: python iterator cycle itertools

Python的itertools.cycle()文档给出了伪代码实现:

def cycle(iterable):
    # cycle('ABCD') --> A B C D A B C D A B C D ...
    saved = []
    for element in iterable:
        yield element
        saved.append(element)
    while saved:
        for element in saved:
              yield element

下面,它说:“注意,这个工具包的成员可能需要大量的辅助存储(取决于可迭代的长度)。”

我基本上是沿着这条路走下去了,除了我做了这个,这不需要创建一个可迭代的副本:

def loop(iterable):
    it = iterable.__iter__()

    while True:
        try:
            yield it.next()
        except StopIteration:
            it = iterable.__iter__()
            yield it.next()

x = {1, 2, 3}

hard_limit = 6
for i in loop(x):
    if hard_limit <= 0:
        break

    print i
    hard_limit -= 1

打印:

1
2
3
1
2
3

是的,我意识到我的实现不适用于str,但可以实现。我更好奇为什么它会创建另一个副本。我有一种感觉它与垃圾收集有关,但我在Python的这个领域没有得到很好的研究。

谢谢!

1 个答案:

答案 0 :(得分:14)

Iterables只能通过迭代

您可以在循环中创建 new iterable。循环不能这样做,它必须与你传入的任何东西一起工作。cycle不能简单地重新创建迭代。因此,它被迫存储原始迭代器产生的所有元素。

如果您传入以下生成器,则loop()会失败:

def finite_generator(source=[3, 2, 1]):
    while source:
        yield source.pop()

现在你的loop()产生了:

>>> hard_limit = 6
>>> for i in loop(finite_generator()):
...     if hard_limit <= 0:
...         break
...     print i
...     hard_limit -= 1
... 
1
2
3

您的代码仅适用于序列,使用cycle()会过多;在这种情况下,您不需要cycle()的存储负担。简化为:

def loop_sequence(seq):
    while True:
        for elem in seq:
            yield elem