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

Posted

技术标签:

【中文标题】当您尝试在迭代列表元素时删除它时会发生啥【英文标题】:What happens when you try to delete a list element while iterating over it当您尝试在迭代列表元素时删除它时会发生什么 【发布时间】:2018-02-07 07:56:11 【问题描述】:

我正在迭代一个列表,如下所示:

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 时对其进行修改,正确的方法是迭代列表的副本。但我想知道幕后究竟发生了什么,即为什么上面的 sn-p [2, 4] 的输出是?

【问题讨论】:

您是否使用调试器或类似pythontutor.com/visualize.html 的工具运行此程序? 我已经使用 ipython shell 运行了这个。 等等,为什么这个问题被否决了?迭代和删除很糟糕,但这个输出让我感到困惑。 第一项被删除,然后列表向左移动,因此索引增加,第二项被保留。以此类推。 unspecified.wordpress.com/2009/02/12/… 看起来 python 也有未定义的行为 【参考方案1】:

您可以使用自制的迭代器来显示(在本例中为 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

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

【讨论】:

@thebjorn 是的,但是迭代它们并更改它们(同时保持大小不变​​)可能会产生有趣的东西:例如 ***.com/questions/45877614/… 和 ***.com/questions/45489688/…。它更难可视化(这就是我所指的),因为内部的 set/dict 结构没有从 Python 中暴露出来。 这让我想起了 Rick Cook (en.wikiquote.org/wiki/Rick_Cook) 的一句话:-)【参考方案2】:

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

我们知道,列表中的每一项都存在于自己的唯一索引中;它们是按顺序排列的,从 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']

【讨论】:

以上是关于当您尝试在迭代列表元素时删除它时会发生啥的主要内容,如果未能解决你的问题,请参考以下文章

当您引用一个 DOM 对象 (var o=docume...) 并通过 parent.innerHTML='' 删除该 DOM 对象时会发生啥?

当您在具有相同基类的派生类之间进行动态转换时会发生啥?

当您在 Oracle 中删除附加了外键的主键约束然后重新启用主键时会发生啥?

Python:如何在迭代列表时从列表中删除元素而不跳过未来的迭代

当您单击应用程序的启动图标时会发生啥?

当您有多个支持相同外部附件协议的应用程序时会发生啥?