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

时间:2017-05-02 23:43:47

标签: python numpy iterator generator cycle

考虑这个例子:

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激发?我认为生成器只能执行一次,因为生成器只返回其当前值并继续运行。

cycle被抛出时,StopIteration只是重新创建生成器吗?当我迭代例如大型numpy数组时,这会成为一个问题(效率低下)吗?

次要问题:这是一种使用迭代器/生成器循环大数据集的“pythonic”方式吗?或者我应该将循环逻辑直接传递给生成器本身(比如定义索引和使用带有重置索引的while循环)?

我的目的是有效地遍历大型数据集(大多数是numpy数组;> 100.000个条目)。

2 个答案:

答案 0 :(得分:3)

  

cycle如何通过StopIteration阻止生成器激发?

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

答案 1 :(得分:1)

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__的'包装器类',然后使用我们新的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