【问题标题】:Python iterator when underlying object changes底层对象更改时的 Python 迭代器
【发布时间】:2014-05-24 02:49:00
【问题描述】:

如果底层对象在迭代期间发生变化,我想知道迭代器的一般行为是什么。

使用一个简单的可变列表,看起来很明显:迭代器将尝试跟随下一个元素(如果有),如果到达末尾则发送StopIteration

>>> l = range(10)
>>> a = iter(l)
>>> a.next()
0
>>> a.next()
1
>>> a.next()
2
>>> l[3]='a'
>>> a.next()
'a'
>>> a.next()
4
>>> del l[5]
>>> a.next()
6
>>> a.next()
7
>>> a.next()
8
>>> a.next()
9
>>> a.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

到目前为止,这是不言自明的。我不明白的是,如果我追加一个新元素,迭代器仍然会返回StopIteration

>>> l.append(11)
>>> a.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

如果我在到达终点之前做同样的事情:

>>> l=[1]
>>> a=iter(l)
>>> a.next()
1
>>> l.append(2)
>>> a.next()
2

这在幕后是如何工作的,更复杂的可变可迭代对象的预期行为是什么? (例如,考虑一个表示图的对象,然后可以使用遍历算法对其进行迭代。如果在迭代时添加/删除节点会发生什么?)

【问题讨论】:

  • 回答:不要。你问在这些情况下应该发生什么,但实际上你自己也偶然发现了答案:这种行为通常是不明确的。

标签: python iterator


【解决方案1】:

PEP 234 (Iterators) 中有关于该特定问题的评论:

一旦特定的迭代器对象引发了 StopIteration,将 它还会在所有后续的 next() 调用中引发 StopIteration?

有人说要求这个会很有用,其他人说 将其对单个迭代器开放是有用的。 请注意,这可能需要一些额外的状态位 迭代器实现(例如函数包装迭代器)。

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

注意:这实际上并没有在 Python 2.2 中实现;有 迭代器的 next() 方法可以引发的许多情况 StopIteration 在一次调用但不是在下一次调用。这已经 在 Python 2.3 中修复。

【讨论】:

  • 这像是建议还是由语言强制执行?如果我实现了一个迭代器类,我是否需要注意一旦 __next__() 返回 StopIteration,它不会返回任何其他内容?
  • 不,没有强制执行。您必须自己实现该行为。
【解决方案2】:

在迭代对象时更改对象有三种典型行为:

  • 将返回新数据
  • 新数据将被忽略
  • 旧数据将被跳过

换句话说:实际行为是未定义的。

在迭代对象时更改对象是一个非常常见的问题,以至于在 Python 3 中,类型 setdict(可能还有其他类型)被更改,如果在迭代期间检测到添加或删除,则会立即引发错误。

【讨论】:

    【解决方案3】:

    有一些迭代器即使在StopIteration 被提升后也可以产生更多数据;这样的迭代器是considered broken

    这并不意味着迭代器有问题——这意味着如果你不小心使用这样的迭代器,最终可能会出现错误和代码损坏。

    【讨论】:

      猜你喜欢
      • 2012-11-20
      • 1970-01-01
      • 1970-01-01
      • 2018-01-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-31
      相关资源
      最近更新 更多