1

我有一个 Receiver 对象,它有时会累积一个数据包队列,这些数据包将在处理时被消耗。让这个接收器有一个迭代器协议似乎是合理的,因此next( receiver )将手动检索下一个数据包,并for packet in receiver遍历当前可用的数据包。直观地说,进行一次这样的迭代就可以了,遍历所有可用的数据包,直到接收器for通过引发来停止循环StopIteration(这是迭代器告诉for循环该停止的标准方式),然后再使用这样的for再次循环以检查在此期间到达的任何新数据包。

但是,Python文档说:

一旦迭代器的__next__()方法 raise StopIteration,它必须在后续调用中继续这样做。不遵守此属性的实现被视为已损坏。

据我所知,即使这段代码被认为是“被认为损坏了”,它也可以正常工作。所以我想知道拥有看似工作正常的代码对我来说有多糟糕,并且以一种直观的方式期望迭代器能够工作,但不知何故“被认为是坏的”?在你提出后,返回更多物品有什么问题StopIteration吗?有什么理由我应该改变这个吗?

(我认识到我可以使接收器成为一个可迭代的(其__iter__方法会产生一些其他迭代器)而不是迭代器本身(使用它自己的__next__方法),但是(a)这不支持熟悉的直观使用next( receiver )将下一个数据包从队列中弹出,并且(b)当我已经有一个非常好的类似迭代器的对象时,重复生成新的迭代器对象似乎既浪费又低效,其唯一的错误是它显然“被认为已损坏”,并且( c) 将接收器呈现为一种可迭代容器会产生误导,因为接收器在检索数据包时会消耗数据包(我正在为其制作 Python 包装器的 C 库中内置的行为,我不认为我也开始在 Python 中缓存它们是有意义的),所以如果有人试图让多个迭代器以他们自己的节奏遍历接收者的队列,迭代器会互相窃取项目并产生比我将其呈现为单个停走迭代器所产生的任何结果更加令人困惑的结果而不是作为一个可迭代的容器。)

4

2 回答 2

0

我将添加另一个不完全令人满意的答案,以防它对有兴趣探索此问题的任何人有所帮助。迭代器在发布后永远不会改变主意的要求StopIteration可以追溯到 2001 年 Python 中迭代器的起源,在PEP 234中,它说:

一旦特定的迭代器对象引发了 StopIteration,它是否还会在所有后续 next() 调用中引发 StopIteration?有人说要求这个会很有用,另一些人说把这个对单个迭代器开放是很有用的。请注意,对于某些迭代器实现(例如,函数包装迭代器),这可能需要额外的状态位。

解决方案:一旦引发 StopIteration,调用 it.next() 将继续引发 StopIteration。

注意:这实际上并没有在 Python 2.2 中实现;在很多情况下,迭代器的 next() 方法可以在一次调用时引发 StopIteration,但在下一次调用时不会。这已在 Python 2.3 中得到解决。

最接近解释禁令的是说“有些人说它会有用”(但也有些人没有)。它还指出“对于某些迭代器实现(例如函数包装迭代器),这可能需要额外的状态位”。但这似乎更多的是禁令的考虑,而不是支持它,因为遵守禁令可能需要迭代器实现添加一个额外的状态位来跟踪它们现在正式退休的事实?我想拥有一个“终生仁慈的独裁者”的缺点之一是他的声明往往没有得到很好的解释!

此外,它说这个禁令并没有在 Python 2.2 中实现,但在 Python 2.3 中已经“补救”了。显然,从那以后的二十年里(!!)一直“没有补救”!或者他们“补救”的只是一些没有遵守这个禁令的特定迭代器,而不是 Python 没有执行这个禁令的事实?

怀疑这里的目标只是告诉人们他们通常可以期望迭代器在停止后继续说他们已经停止,而不是冒险导致错误或进入无限循环或产生无意义的返回值,如果你不小心再试next()一次。但似乎更好的声明是禁止这些“不良行为”,而不是禁止动机良好的设计,即迭代器在暂时停止后故意产生更多“好”值。不幸的是,这种怀疑可能不足以向任何人保证违反此禁令没有任何隐患。

于 2021-11-28T22:18:31.350 回答
-1

另一个(现已删除)的答案指出,Python 的内置文件对象会生成一个迭代器,该迭代器可以而且通常会在停止后重新启动,这证明了走走停停的迭代器可以完美运行,而不是他们正式说迭代器“应该”工作的方式。

这是另一个简单的示例,说明迭代器可以在 raise 之后再次活跃起来StopIteration。但这并不能解释为什么 python 文档不鼓励以这种方式做事,也不能解释这样做的潜在危险(如果有的话)。

class StopAfterEachWord:
    def __init__(self, phrase = "stop and go"):
        self.phrase = phrase
        self.i = -1

    def __iter__(self): return self

    def __next__(self):
        self.i += 1
        if self.i >= len(self.phrase) or self.phrase[self.i]==' ': raise StopIteration
        return self.phrase[self.i]

it = StopAfterEachWord("stop and go")
for letter in it: print(letter)
print("The iterator has now stopped.")
for letter in it: print(letter)
print("The iterator stopped again.")
for letter in it: print(letter)

在线尝试!

此示例使用一个名为 的迭代器it。后两个for循环说明了这个迭代器可以继续工作,即使它已经提升StopIteration到停止前面的for循环。

于 2021-11-28T21:41:08.180 回答