1. 问题场景
删除列表中的正数部分
my_list = [i for i in range(10)]
for it in my_list:
if it>0:
my_list.remove(it)
print(my_list)
out: [0, 2, 4, 6, 8]
2. 问题分析
for … in 遍历中,是利用下标遍历的,这个下标从开始就固定了:0~9
而随着remove,会造成list内元素的自动补位,从而在遍历过程中漏掉了被删元素的下一个元素
3. 解决方案
两种解决方案:使用filter函数滤波,或者倒序遍历进行删除(del或remove)
(注意,python3中filter返回一个迭代器,因此需要强制转换为list)
源码如下:
from time import time
# 初始化
length = 10000
my_list1 = [i for i in range(length)]
my_list2 = [i for i in range(length)]
my_list3 = [i for i in range(length)]
# 方法一
time1 = time()
my_list1 = list(filter(lambda x: x<=0, my_list1))
time2 = time()
# 方法二
for i in range(len(my_list2)-1, -1, -1):
if my_list2[i]>0:
del(my_list2[i])
time3 = time()
# 方法三
for i in range(len(my_list3)-1, -1, -1):
if my_list3[i]>0:
my_list3.remove(my_list3[i])
time4 = time()
# 性能评估
print("列表维度:"+str(length))
print("方法一用时:"+str(round(time2-time1,5)))
print("方法二用时:"+str(round(time3-time2,5)))
print("方法三用时:"+str(round(time4-time3,5)))
4. 性能评估
(单位:秒)
5. 进阶
filter也能够处理字典等序列,比如下面这个场景:
a 是由四个字典组成的列表
a = [
{'id':1, 'w':1, 'h':1},
{'id':1, 'w':2, 'h':2},
{'id':2, 'w':3, 'h':3},
{'id':2, 'w':4, 'h':4},
]
需求:仅保留’id’==1的两个字典,且删除其中的’id’键值对应的项,即期望输出为:
a = [
{'w':1, 'h':1},
{'w':2, 'h':2},
]
方法:使用两次 filter 进行滤波,第一次过滤‘id’==1的内容,第二次去除字典中键值为’id’的项
a = [
{'id':1, 'w':1, 'h':1},
{'id':1, 'w':2, 'h':2},
{'id':2, 'w':3, 'h':3},
{'id':2, 'w':4, 'h':4},
]
b = [dict(
filter(lambda y: y[0]!='id', x.items())
)
for x in
filter(lambda x: x['id']==1, a)]
print(b)
[Out]:
[{'w': 1, 'h': 1}, {'w': 2, 'h': 2}]
6. 惰性运算
需要注意的是,使用filter返回的对象,只可以访问一次,如下面两个例子:
a = [1,2,3,4,5]
f = filter(lambda x: x>2, a)
print(f)
print(list(f))
print(f)
print(list(f))
[Out]:
<filter object at 0x000001DB7EF8BE48>
[3, 4, 5]
<filter object at 0x000001DB7EF8BE48>
[]
a = [1,2,3,4,5]
f = filter(lambda x: x>2, a)
print('第一次访问:')
for i in f:
print(i)
print('第二次访问:')
for i in f:
print(i)
[Out]:
第一次访问:
3
4
5
第二次访问:
可以发现,不论是转成list,还是直接访问,都只能操作一次~这是由于 filter 产生的迭代器使用了“惰性运算”,它仅供迭代一次,也就是它只有被使用的时候才被取出或计算出,放到内存中,访问之后随即销毁。这可以提升性能,节约内存。