自定义python生成器中的Return和StopIteration

时间:2019-05-17 15:26:04

标签: python python-3.x exception generator

在Python中阅读了一些新样式的观点,并探讨了在生成器中从StopIterationreturn的转变。我的主要问题是这在定制生成器中应该如何工作。我有一堂课,在这里我直接重写__next__方法,因为在跟踪生成时必须添加一些逻辑。

下面的内容与我正在执行的操作非常接近,具有所有关键要素。请注意,我实际上并不是在创建一个复制列表的生成器,只是将其作为一个最小的示例。

class Test():
    items = <list>

    def __init__(self):
        self.index = 0

    def __next__(self):
        if self.index >= len(self.items):
            raise StopIteration
        value = self.items[self.index]
        self.index += 1
        return value

    def reset(self):
        self.index = 0

因此,在这种情况下,我要遍历一个列表,直到耗尽为止,然后下游调用将决定是重置生成器还是在耗尽后继续运行。但是,由于不赞成使用StopIteration,如何启用类似的功能?在这里使用return来引发StopIteration的标准建议似乎并不适用,我真的希望不要更改下游代码来检查生成Nones

的生成器

那么我应该在这里做什么? StopIteration仍可接受__next__例外吗?

2 个答案:

答案 0 :(得分:3)

StopIteration是不是被弃用,您只是误解了什么是生成器。您实际上没有生成器,您有一个 iterator 。生成器只是使用yield创建迭代器的函数。

您正在创建自己的基本迭代器实现,而无需使用生成器。完成后,迭代器会从StopIterator引发__next__。您的代码可以在此处正确执行。

摘自Python数据模型文档的Generator Types section

  

Python的generator提供了一种实现迭代器协议的便捷方法。如果将容器对象的__iter__()方法实现为生成器,它将自动返回提供__iter__()__next__()方法的迭代器对象(从技术上讲,是生成器对象)。可以在yield表达式的文档中找到有关生成器的更多信息。

根据同一文档,在Iterator Types section中:

  

iterator.__next__()
   从容器返回下一个项目。如果没有其他项目,请提出StopIteration例外。

已弃用,但这仅涉及在生成器函数中使用StopIteration。参见PEP 479 - Change StopIteration handling inside generators

  

此PEP建议对生成器进行更改:将StopIteration放在生成器内部时,将其替换为RuntimeError。 (更确切地说,当异常将在生成器的堆栈框架中冒泡时会发生这种情况。)由于更改是向后不兼容的,因此该功能最初是使用__future__语句引入的。

在生成器内部,使用return会触发StopIteration异常。手动引发StopIteration实际上会产生难以理解的错误,因为生成迭代器的任何使用者都无法区分正确使用异常和不正确,偶然 {{ 1}}来自您的生成器的异常。这使得此类问题难以调试,这就是为什么在将来的Python版本中它们在生成器函数中的使用正在改变的原因。

侧面说明:您的实现需要使用iterator.__iter__() method,它仅返回StopIteration

答案 1 :(得分:0)

如果您想要一个可重置的包装式生成器,一种可能的选择是将其数据保留在其他位置:

class Gen:
   def __init__(self):
      self.items = []
   def restart(self):
      for x in self.items:
         yield x

g = Gen()
for x in g.restart():
   pass
for x in g.restart():
   pass