循环如何防止生成器抛出 StopIteration 异常?

How does cycle prevent a generator from throwing a StopIteration exception?

考虑这个例子:

from itertools import cycle

def foo():
    for i in range(3):
        yield i

c = cycle(foo())

next(c)  # -> 0
next(c)  # -> 1
next(c)  # -> 2 [StopIteration should be thrown here normally]
next(c)  # -> 0
...

cycle 如何防止发电机通过 StopIteration 励磁?我认为生成器只能执行一次,因为生成器仅 returns 其当前值并继续前进。

抛出 StopIterationcycle 只是重新创建生成器吗?当我遍历大型 numpy 数组时,这会成为一个问题(效率低下)吗?

第二个问题:这是"pythonic"用iterators/generators循环大数据集的方法吗?或者我应该将循环逻辑直接转移到生成器本身(比如定义索引和使用带有重置索引的 while 循环)?

我的目的是有效地循环大型数据集(主要是 numpy 数组;>100.000 个条目)。

How does cycle prevent the generator from exciting via a StopIteration?

没有。生成器到达终点并正常退出 StopIterationcycle 存储生成器的输出,当 cycle 看到 StopIteration 时,它会切换到从生成器生成的存储历史记录中生成项目。

user2357112 已经回答了你的第一个问题。

关于你的第二个,对于像 numpy 数组这样的容器,我们可以创建一个等效于循环,不需要在内存中制作额外的副本。请注意,这不适用于生成器!如果我们要一遍又一遍地使用它们,我们必须在某个地方存储一个副本,或者至少有一种方法可以按需生成它们。

def cycle(container):
    if iter(container) is container:
        raise TypeError('need a container, not a generator')
    while True:
       yield from container



# this works correctly for a container
for i, char in enumerate(cycle('abc')):
    print(char)
    if i > 10:
        break

假设我们想重复读取文件而不在内存中创建每一行的副本。

我们可以创建一个实现 __iter__ 的 'wrapper class',然后使用我们新的 cycle 方法。

class Reader():
    def __init__(self, path, *args, **kwargs):
        self.path, self.args, self.kwargs = path, args, kwargs
    def __iter__(self):
        with open(self.path, *self.args, **self.kwargs) as file:
            yield from file

 #eg:
 for line in cycle(Reader(filepath)):
     #somecode