在Python中阅读了一些新样式的观点,并探讨了在生成器中从StopIteration
到return
的转变。我的主要问题是这在定制生成器中应该如何工作。我有一堂课,在这里我直接重写__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__
例外吗?
答案 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