当您尝试在遍历列表元素时删除它会发生什么

What happens when you try to delete a list element while iterating over it

我正在按如下方式迭代列表:

some_list = [1, 2, 3, 4]
another_list = [1, 2, 3, 4]

for idx, item in enumerate(some_list):
    del some_list[idx]

for item in another_list:
    another_list.remove(item)

当我打印出列表的内容时

>>> some_list
[2, 4]
>>> another_list
[2, 4]

我知道 Python 不支持在迭代时修改 list,正确的方法是迭代列表的副本。但我想知道幕后到底发生了什么,即为什么上面代码片段的输出是 [2, 4]?

您可以使用自制的迭代器来显示(在本例中 prints)迭代器的状态:

class CustomIterator(object):
    def __init__(self, seq):
        self.seq = seq
        self.idx = 0

    def __iter__(self):
        return self

    def __next__(self):
        print('give next element:', self.idx)
        for idx, item in enumerate(self.seq):
            if idx == self.idx:
                print(idx, '--->', item)
            else:
                print(idx, '    ', item)
        try:
            nxtitem = self.seq[self.idx]
        except IndexError:
            raise StopIteration
        self.idx += 1
        return nxtitem

    next = __next__  # py2 compat

然后在您要检查的列表周围使用它:

some_list = [1, 2, 3, 4]

for idx, item in enumerate(CustomIterator(some_list)):
    del some_list[idx]

这应该说明在那种情况下会发生什么:

give next element: 0
0 ---> 1
1      2
2      3
3      4
give next element: 1
0      2
1 ---> 3
2      4
give next element: 2
0      2
1      4

虽然它只适用于序列。映射或集合更复杂。

I want to know what exactly happens behind the scenes

我们知道,列表中的每个项目都有自己唯一的索引;这是有序的,从 0 开始。如果我们删除一个项目,那么索引大于我们删除的项目的任何项目现在都被向下移动。

这就是重要的原因:

foo = ['a', 'b', 'c', 'd']
for index in range(len(foo)):
    del foo[index]

在这个循环中,我们将删除所有元素,所以我们应该以 foo == [] 结束,对吧?不是这种情况。在我们第一次循环中,我们删除索引 0 处的项目,索引 1 处的项目成为索引 0 处的项目.下一次循环时,我们删除 索引 1 处的项目,它是 之前索引 2.[=26 处的项目=]

在前两次迭代中,我们从数组中删除了 'a''c',*但我们忽略了删除 'b'。一旦我们到达第三次迭代(虽然我们会删除索引 2), 不再是索引 2 处的元素;只有索引 01。当我们尝试删除索引 2 处不存在的项时会引发异常,并且循环会停止。结果是一个损坏的数组,如下所示:['a', 'd'].