Python中的for循环迭代次数比我预期的少

4
我原以为下面的循环会迭代六次,而实际上使用python3只迭代了三次。我不理解这种行为。 我知道当我删除元素时,列表会发生变化,但我不知道它如何影响for循环的条件。 为什么这个循环迭代的次数少于六次?
a = [1, 2, 3, 4, 5, 6]
for elem in a:
        del a[0]
        print(a)

1
提示:列表迭代器提供第一个、第二个、第三个...项。 - MisterMiyagi
7
通常而言,如果你在迭代集合时修改了该集合,那么你注定会失败。 - Felk
3个回答

4
您在循环的每次迭代中通过 del a[0] 删除了第一个元素,因此迭代器在3步之后就被清空了,因为它会在下一次迭代时移动到您删除的那个元素的后面一个元素。 您可以在下面的代码中检查迭代器当前所在的元素和列表状态。
a = [1, 2, 3, 4, 5, 6]
for elem in a:
    print(elem)
    del a[0]
    print(a)

输出为:
1
[2, 3, 4, 5, 6]
3
[3, 4, 5, 6]
5
[4, 5, 6]

你可以把它想像成一个指针,指向列表的第一个元素。当你在每个迭代中删除第一个元素时,这个指针会跳过2个步骤,而且对于6个元素,它只能跳3次。
通常修改你正在迭代的同一列表是一个坏主意。但如果你真的想要这么做,你可以迭代列表的副本 a[:] ,如果你真的想要删除项目的话。
a = [1, 2, 3, 4, 5, 6]
for elem in a[:]:
    del a[0]
    print(a)

输出结果为:
[2, 3, 4, 5, 6]
[3, 4, 5, 6]
[4, 5, 6]
[5, 6]
[6]
[]

嗯,好的,我明白了,谢谢。因此,在每次迭代之前都会检查列表a。经过三次迭代后,列表中只剩下三个元素,因此没有第四个要获取的元素。起初我并没有理解“迭代器在3个步骤中被清空”的表达,但现在我想我明白了。谢谢。 - Zois Tasoulas
把它想象成一个指针,指向列表的第一个元素,每次迭代删除第一个元素时,该指针跳2步,对于6个元素,它只能跳3次。@Zois - Devesh Kumar Singh
谢谢!还有一个小错别字在 del [0],缺少了 a。它不允许我编辑它。 - Zois Tasoulas

2

在CPython中,list迭代器通过迭代列表的位置来工作。你可以把它看作是这样工作的:

最初的回答:

CPython中的list迭代器通过遍历列表的位置来工作。你可以将其想象为以下方式:

def list_iter(items: list):
    index = 0
    while True:
        yield items[index]
        index += 1

换句话说,迭代提供了第0项,然后是1、2等。没有预取项目-需要时从列表中查找项目。
由于在每个步骤中删除第一项,因此列表在每个步骤中缩短1个。由于您从6个项目的列表开始,在第三次迭代中,它减少到3个项目-这意味着第四次迭代无法查找项目。因此,您的迭代在三个步骤后结束。
当在每个循环中打印当前元素时,您可以看到这一点。为了可视化效果,请使用enumerate来获取迭代的索引。请注意,它会按一个索引前进,但值也会移动,总偏移量为两个。
>>> a = [1, 2, 3, 4, 5, 6]
... for idx, elem in enumerate(a):
...     print(elem, 'from', a)
...     print('      ', '   '*idx, '^')
...     del a[0]
...
1 from [1, 2, 3, 4, 5, 6]
        ^
3 from [2, 3, 4, 5, 6]
           ^
5 from [3, 4, 5, 6]
              ^

在遍历容器时修改它通常是不明确的。您应该遍历一个副本:

最初的回答:

通常情况下,在迭代容器时修改它是不被允许的。相反,您应该遍历一个副本来进行操作。

a = [1, 2, 3, 4, 5, 6]
for elem in a.copy():
    del a[0]
    print(a)

1
很好的解释。谢谢@MisterMiyagi!有一个问题 - 如果第一个代码片段,最后一行应该是index += 1吗? - Supratim Haldar
1
@SupratimHaldar 是的,谢谢你发现这个错误。我已经修复了它。 - MisterMiyagi

1

同时迭代和删除列表中的元素比较棘手。一个处理方法是以相反的顺序遍历列表:

a = [1, 2, 3, 4, 5, 6]
for elem in reversed(a):
    print(a)
    del a[0]
print(a)

它会打印:

[1, 2, 3, 4, 5, 6]
[2, 3, 4, 5, 6]
[3, 4, 5, 6]
[4, 5, 6]
[5, 6]
[6]
[]

有趣。你能加上几行来解释一下这里发生了什么吗?我很好奇,为什么在这种情况下 reverse(a) 起作用。 - Supratim Haldar
1
倒退不会破坏迭代器,@MisterMiyagi在他的回答中解释得更好。 - Óscar López
好的。谢谢 @Óscar López! - Supratim Haldar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接