【问题标题】:How does cycle prevent a generator from throwing a StopIteration exception?循环如何防止生成器抛出 StopIteration 异常?
【发布时间】:2017-09-30 15:02:22
【问题描述】:

考虑这个例子:

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 个条目)。

【问题讨论】:

    标签: python numpy iterator generator cycle


    【解决方案1】:

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

    它没有。生成器到达其末端并像往常一样以StopIteration 退出。 cycle 存储生成器的输出,当cycle 看到StopIteration 时,它会切换到从生成器生成的存储历史记录中生成项目。

    【讨论】:

    • 这听起来像cycle 如果我在大型数据集上使用它会非常低效吗?我的意思是它必须存储yield 返回的每一个值?
    • @daniel451:它真的没有任何其他选择。您可能有其他选择,因为您有更多上下文,但 cycle 没有。
    【解决方案2】:

    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
    

    【讨论】:

    • 最后一点,你总是可以使用额外的 while 或 for 循环来代替 cycle,如果它是生成器,则首先调用 list() 或 tuple()。这不太“干净”,但对您的目标受众来说可能更清楚,这取决于他们对 python 和 itertool 风格的习语的熟悉程度。
    猜你喜欢
    • 1970-01-01
    • 2018-12-23
    • 2013-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-07
    • 2020-07-24
    • 1970-01-01
    相关资源
    最近更新 更多