当您尝试在迭代时删除列表元素时会发生什么

时间:2017-08-29 18:42:31

标签: python list for-loop python-internals

我按如下方式迭代列表:

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,而正确的方法是迭代列表副本(所以请不要downvote)。但我想知道幕后究竟发生了什么,即为什么输出上面的代码段[2, 4]

2 个答案:

答案 0 :(得分:8)

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

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

它仅适用于序列。它对于映射或集合来说更复杂。

答案 1 :(得分:1)

  

我想知道幕后究竟发生了什么

众所周知,列表中的每个项目都有自己独特的索引;从0开始按顺序排列。如果我们删除一个项目,那么索引大于我们删除的项目的任何项目现在都已向下移动。

这就是重要的原因:

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

在这个循环中,我们删除了所有元素,因此我们最终应该使用foo == [],对吧?不是这种情况。在我们第一次循环浏览时,我们删除索引0处的项目,索引1 的项目成为索引0 的项目。我们下次循环时,会删除索引为1 的项目,该项目以前是索引2的项目

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