以下代码:
a = list(range(10))
remove = False
for b in a:
if remove:
a.remove(b)
remove = not remove
print(a)
使用 Python 3.2 时,输出
[0, 2, 3, 5, 6, 8, 9]
,而不是 [0, 2, 4, 6, 8]
。
请注意,我并不是要解决这种行为,而是要理解它。
我为回答这个问题争论了一段时间,因为类似的问题已经在这里被问过很多次了。但它的独特性足以让人们相信它是无罪的。 (不过,如果其他人投票结束,我不会反对。)这是对正在发生的事情的直观解释。
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] <- b = 0; remove? no
^
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] <- b = 1; remove? yes
^
[0, 2, 3, 4, 5, 6, 7, 8, 9] <- b = 3; remove? no
^
[0, 2, 3, 4, 5, 6, 7, 8, 9] <- b = 4; remove? yes
^
[0, 2, 3, 5, 6, 7, 8, 9] <- b = 6; remove? no
^
[0, 2, 3, 5, 6, 7, 8, 9] <- b = 7; remove? yes
^
[0, 2, 3, 5, 6, 8, 9] <- b = 9; remove? no
^
既然没有人知道,我将尝试回答您的其他问题:
为什么没有给出错误表明底层迭代器正在被修改?
要在不禁止许多完全有效的循环构造的情况下抛出错误,Python 必须了解大量关于正在发生的事情,并且可能必须在运行时获取该信息。所有这些信息都需要时间来处理。它会让 Python 变慢很多,尤其是在速度真正重要的地方——循环。
与早期版本的 Python 相比,这种行为的机制是否发生了变化?高度简而言之,不。或者至少我
怀疑它,而且自从我学习Python(2.4)以来它确实是这样的。坦率地说,我希望可变序列的任何直接实现都以这种方式表现。哪位知道的比较清楚的请指正。 (实际上,快速文档查找确认了 Mikola 引用的文本自 1.4 版以来一直存在于教程中!)
但在我看来,更有趣的问题是为什么 python 在发生这种情况时不选择生成错误消息。如果您尝试修改字典,它确实会产生这样的错误消息。我认为有两个原因。
因此代码:
a = list(range(10))
remove = True
for b in reversed(a):
if remove:
a.remove(b)
remove = not remove
print(a)
产生预期的结果:
[0, 2, 4, 6, 8]
第二次迭代,您位于序列的位置 [1],并且删除了“1”。然后迭代器将您带到序列中的位置 [2],现在是“3”,因此“2”会被跳过(因为“2”由于被删除而现在位于位置 [1])。当然,“3”不会被删除,因此您继续到序列中的位置 [3],现在是“4”。它被删除,带你到位置 [5],现在是“6”,依此类推。
您要删除内容的事实意味着每次执行删除时都会跳过一个位置。