Python陷阱–数组-循环删除
1. 前言
今天去面试了,问到数组的循环删除问题,算是Python需要注意的地方之一吧。一起回顾一下吧。
问题如下:
取 li = [1, 2, 3, 4, 5, 6],运行如下代码后结果如何:
#问题一
li = [1, 2, 3, 4, 5, 6]
for i in range(len(li)):
if li[i] = 2:
li.pop(i)
print(li)
#问题二
li = [1, 2, 3, 4, 5, 6]
for i in li:
li.remove(i)
print(i)
1.1 简单介绍
相信大家都对答案并不陌生,这里为初学不久的萌新们(比如本山人)简单分析一下。
问题一中的代码会报错,原因是删除元素2
后,列表的长度会减小(6 -> 5)但是遍历的区间不会变range(6)
所以会导致索引超出范围,IndexError
问题二中的列表无法被清空,原因是删除时列表内容的变动,如下所示:
[外链图片转存失败
2. 解决方案
关于问题二解决方案如下:
2.1 蠢办法
该方法仅用于提供思路,分析问题,如果时间紧张请参考2.2,挤出来的时间可以多陪陪女朋友(可拉倒吧,认真看)。
经分析:问题二的错误之处在于遍历删除元素的同时导致列表内容发生变化,从而每次循环开始,指针移动时会“漏掉”元素,所以解决问题的办法在于保证遍历的过程中,指针将要指到的部分列表内容不会变化。
上解决方案:
#蠢办法,建立副本
li = [1, 2, 3, 4, 5, 6]
li_copy = li.copy() #浅拷贝 li[:]也可
for i in li_copy:
li.remove(i)
print(i)
- 从内存空间分析 ,增加了消耗,不好。
2.2 巧办法
经2.1分析得,只需保证遍历的过程中,指针将要指到的部分列表内容不会变化即可。结合每个遍历过程开始时,指针只会后移。巧用反向遍历,解决问题。
li = [1, 2, 3, 4, 5, 6]
for i in li[::-1]:
li.remove(i)
关于li[::-1]
是否会造成额外开销问题:
In [1]: l = [257,2,3,4,5,6]
In [2]: print(id(l[0]))
140307042424592
In [3]: for i in l[::-1]:
...: print(i, id(i))
...:
6 10919488
5 10919456
4 10919424
3 10919392
2 10919360
1 140307042424592
In [4]: print(id(l))
140218462889544
In [5]: print(id(l[::-1]))
140218464334344
- 答案是,反向迭代不会产生额外开销,反向迭代
l[::-1]
并非是l
的副本。