python循环中慎用pop()函数和remove()函数

本文以列表作为示例,讲述为什么在循环体中不能使用pop()函数和remove()函数。

出现的问题

一般地,删除列表中的一个元素有pop()remove()方法。例如下面的例子:

lst = [1, 2, 3, 4 ,5]
print(lst)
lst.pop()
print(lst)
lst.remove(1)
print(lst)

结果为

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

但是在循环体中不能怎么做,列表循环一般有两种方式,一种是for i in range(len(lst)),另外一种是for item in lst

  • 第一种循环
num_list = [1, 2, 3, 4, 5]
print(num_list)

for i in range(len(num_list)):
    if num_list[i] == 2:
        num_list.pop(i)
    else:
        print(num_list[i])

print(num_list)

输出结果

[1, 2, 3, 4, 5]
1
4
5
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-25-7e590a65a8c9> in <module>
      3 
      4 for i in range(len(num_list)):
----> 5     if num_list[i] == 2:
      6         num_list.pop(i)
      7     else:

IndexError: list index out of range

可以看到程序报错,出错的原因是当删除了列表中的元素2时,num_list的长度变为了4,所以在循环到最后一个元素的时候,会提示下标超出范围。

  • 第二种循环
num_list = [1, 2, 3, 4, 5]
print(num_list)

for item in num_list:
    if item == 2:
        num_list.remove(item)
    else:
        print(item)

print(num_list)

输出结果为

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

奇怪的发现,元素3怎么丢失了?其实这和python循环中的迭代器有关,当迭代器到达num_list中的第二个元素2的时候,将其删除,然后迭代器将会自动的指向列表的下一个元素,由于这时候已经删除了第二个元素,所以迭代器便指向了新数组的第3个元素也就是4,所以出现了上述的情况,下面的图可以比较清晰的解释上面的过程。
在这里插入图片描述


仔细观察可以发现,虽然上述的结果中间打印的时候漏掉了一个元素,对最终的结果没有什么影响,但是这只是一种比较幸运的情况,比如下面的例子就没那么幸运了。

x = ['a', 'b', 'c', 'd']
y = ['b', 'c']
for i in x:
    if i in y:
        x.remove(i)
print(x)

输出结果为

['a', 'c', 'd']

按照正常的理解,应该最终的结果为['a', 'd'],但是最终结果却多了一个'c',我们根据上述的迭代图进行分析,当遍历到列表x'b'元素时,判断条件成立,删除列表x中的'b'元素,这是列表x=['a', 'c', 'd'],但此时的迭代器位于列表的第三个元素,也就是新列表x'd'元素,直接把'c'忽略掉了,所以最终结果才会出现'c'


解决方法

要想解决这个问题,一种方法自然是定义一个新的数组,将结果存到新数组中,或者从后面往前面遍历,这时候也可以避免上述漏掉元素的情况。

  • 方法1:定义一个新的列表
x = ['a', 'b', 'c', 'd']
y = ['b', 'c']
new_list = list()
for i in x:
    if i in y:
        x.remove(i)
    else:
        new_list.append(i)
print(new_list)
  • 方法2:从后面往前遍历列表
x = ['a', 'b', 'c', 'd']
y = ['b', 'c']
for i in range(len(x) - 1, -1, -1):
    if x[i] in y:
        x.pop(i)
print(x)

两种方法的输出结果都一样

['a', 'd']

不过还是建议使用第一种方法,虽然更占内存,但是简洁易懂,也容易对新列表进行操作。

参考

https://www.cnblogs.com/bananaplan/p/remove-listitem-while-iterating.html
https://kyle.ai/blog/6565.html

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值