Python-遍历列表时删除元素

python中遍历列表的同时去删除元素,其实是个小坑,对于初学者比较容易犯错;我记得是2013年时,遇到过类似的问题,当时也不清楚为什么出一些奇怪的问题/报错,反正用深拷贝就绕过去了。最近看到一篇文章提到了,我也找资料回顾记录一下。

问题描述

错误代码1:

list1 = [1, 2, 3, 4, 5, 2, 2]
for i in list1:
    if i == 2:
        list1.remove(i)
print(list1)

运行输出为:[1, 3, 4, 5, 2]
不符合预期,最后一个2没有被删掉。

如下的错误代码2,这么写,会遇到语法报错。

list1 = [1, 2, 3, 4, 5, 2, 2]
for i in range(len(list1)):
    if list1[i] == 2:
        list1.remove(list1[i])
print(list1)

得到如下报错信息:

Traceback (most recent call last):
  File "/Users/jay/workspace/python.git/py2022/remove_item_in_for_list.py", line 3, in <module>
    if list1[i] == 2:
IndexError: list index out of range

问题分析

Python中用for发起任何形式的遍历时,它的遍历顺序都是从最初就确定的,而在遍历中删除了元素会导致当前索引的变化。错误1中,遍历中间取到的索引的那个值已经变了,会漏掉一些元素。错误2中,是删除元素后列表长度缩小了,所以会超出索引范围。

推荐几种正确的写法

1. 使用filter() 函数过滤符合条件的元素
list1 = [1, 2, 3, 4, 5, 2, 2]
list2 = list(filter(lambda x: x!=2, list1))
print(list2)
2. 列表推动(列表解析式)
list1 = [1, 2, 3, 4, 5, 2, 2]
list2 = [i for i in list1 if i!=2]
print(list2)
3. 拷贝列表,遍历新列表时删除原列表中的内容 (或 相反)

通过列表切片生成新列表:

list1 = [1, 2, 3, 4, 5, 2, 2]
for i in list1[:]:
    if i == 2:
        list1.remove(i)
print(list1)

通过深拷贝deepcopy生成新列表:

import copy

list1 = [1, 2, 3, 4, 5, 2, 2]
list2 = copy.deepcopy(list1)
# 这种简单类型下浅拷贝也会正常;但list1的元素一旦有可变元素,就一定是要深拷贝才正确。
for i in list1:
    if i == 2:
        list2.remove(i)
print(list2)

4. 用while循环来做条件判断

while 2 in list1:
    list1.remove(2)
print(list1)

我个人喜欢用第2种列表推导 和 第3种复制列表 的方法。

参考:
https://segmentfault.com/a/1190000007214571
https://www.jianshu.com/p/230450957649

master

Stay hungry, stay foolish.

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

*