为什么在迭代时修改序列不安全?

19 投票
3 回答
4893 浏览
提问于 2025-04-16 01:55

在循环中修改正在遍历的序列是不安全的(这只会发生在可变序列类型,比如列表)。如果你需要修改正在遍历的列表(比如,想要复制选中的项目),你必须遍历一个副本。使用切片表示法可以让这个过程变得特别方便:

   >>> for x in a[:]: # make a slice copy of the entire list
   ...    if len(x) > 6: a.insert(0, x)
   ... 
   >>> a
   ['defenestrate', 'cat', 'window', 'defenestrate']

为什么不能直接用 for x in a 呢?

3 个回答

4

当你在遍历一个集合(比如列表)的时候,如果你同时对这个集合进行修改,迭代器可能会出现意想不到的行为,比如漏掉某些项目或者重复返回同一个项目。

我运行的这段代码会无限循环:

>>> a = [ 'foo', 'bar', 'baz' ]
>>> for x in a:
...    if x == 'bar': a.insert(0, 'oops')

这是因为迭代器使用一个数字索引来跟踪它在列表中的位置。当你在列表的开头添加一个新项目时,迭代器会再次返回项目 'bar',而不是继续到下一个项目,因为插入操作把项目的位置向前移动了。

13

这是很多编程语言中常见的问题。如果你有一个线性的数据结构,比如一个列表,而你正在遍历这个列表,就需要有某种方式来记录你在列表中的位置。这可能是一个当前的索引,或者一个指针,简单来说就是一个指向“当前位置”的标记。

如果在遍历的过程中你修改了这个列表,比如删除了某个元素,那么这个标记可能就会出错。

一个常见的问题是,当你删除了标记指向的那个元素后,列表中的其他元素会往前移动一位。接下来再进行一次循环时,标记会增加,这样你就可能不小心跳过了一个元素。

有些数据结构的实现允许你在遍历时删除元素,但大多数情况下是不允许的。

17

不想说得太复杂:

如果你在Python中遍历一个可以改变的序列(比如列表),而这个序列在你遍历的时候被修改了,那结果可能就不太明确了。如果你在遍历的时候往序列里插入了一个元素,那么现在什么才算是“下一个”元素呢?如果你删除了下一个对象,又会怎样呢?

因此,在改变一个可变序列的同时去遍历它,会导致一些不确定的情况发生。具体会发生什么,取决于这个列表是怎么实现的。:-)

撰写回答