考虑这个例子:
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个条目)。
答案 0 :(得分:3)
cycle
如何通过StopIteration
阻止生成器激发?
它没有。生成器到达终点并正常退出StopIteration
。 cycle
存储生成器的输出,当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