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

sta*_*onk 9 python iterator cycle python-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
Run Code Online (Sandbox Code Playgroud)

下面,它指出:"注意,该工具包的这个成员可能需要大量的辅助存储(取决于可迭代的长度)."

我基本上是沿着这条路走,除了我这样做,这不需要创建iterable的副本:

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
Run Code Online (Sandbox Code Playgroud)

打印:

1
2
3
1
2
3
Run Code Online (Sandbox Code Playgroud)

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

谢谢!

Mar*_*ers 14

Iterables只能迭代一次.

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

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

def finite_generator(source=[3, 2, 1]):
    while source:
        yield source.pop()
Run Code Online (Sandbox Code Playgroud)

现在你的loop()产品:

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

你的代码只适用于序列,对于这些序列,使用cycle()会过度; cycle()在这种情况下你不需要存储负担.简化为:

def loop_sequence(seq):
    while True:
        for elem in seq:
            yield elem
Run Code Online (Sandbox Code Playgroud)

  • @stantonk:迭代器可以是任何*.它们不必是列表或集合.并且"循环"无法知道这一点.它只能**依赖于迭代器协议.该协议说你只能迭代一次.然后迭代器完成,它将不再生成元素. (4认同)