对象变化时的Python迭代器

3 投票
3 回答
699 浏览
提问于 2025-04-18 02:11

我想了解一下,当一个对象在迭代过程中发生变化时,迭代器一般会有什么样的表现。

以一个简单的可变列表为例,这点似乎很明显:迭代器会尝试继续获取下一个元素,如果没有了,就会返回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

那么这背后是怎么运作的?对于更复杂的可变可迭代对象,预期的行为又是什么呢?(比如,想象一个表示图的对象,然后可以使用遍历算法进行迭代。如果在迭代时添加或删除节点,会发生什么?)

3 个回答

0

有一些迭代器,即使在抛出StopIteration之后仍然可以继续提供数据;这样的迭代器被称为损坏的

这并不是说迭代器本身有问题——而是说,如果你不小心使用这样的迭代器,可能会导致程序出错或者代码出现问题。

0

在遍历一个对象时,改变这个对象通常会出现三种典型的情况:

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

换句话说:实际的行为是不可预测的。

在遍历对象时修改它的内容是一个非常常见的问题,因此在Python 3中,像setdict这样的类型(可能还有其他类型)被修改为在遍历时如果检测到添加或删除操作,就会立即抛出错误。

2

在关于这个问题的讨论中,有人提到了PEP 234(迭代器):

如果一个迭代器对象已经抛出了StopIteration(停止迭代),那么在后续的next()调用中,它还会继续抛出StopIteration吗?

有些人认为应该强制要求这样做,这样会更有用;而另一些人则认为让每个迭代器自己决定更好。需要注意的是,这可能会让某些迭代器的实现需要额外的状态标志(比如,函数包装的迭代器)。

解决方案是:一旦抛出了StopIteration,调用它的next()方法会继续抛出StopIteration

注意:实际上在Python 2.2中并没有实现这个功能;在很多情况下,迭代器的next()方法在一次调用中可能会抛出StopIteration,但在下一次调用中却不会。这在Python 2.3中得到了修复。

撰写回答