之前发过一些坑,请参考Python编程中一定要注意的那些“坑”(一)和Python函数默认值参数的2个坑
今天说说列表的内存自动伸缩带来的坑。
假设有个列表如下,现在下删除其中的所有1,于是编写了如下代码
>>> x = [1, 2, 1, 2, 1, 2, 1, 2] >>> for item in x: if item == 1: x.remove(item)
>>> x [2, 2, 2, 2]
看上去完全正确,但实际上这段代码是有问题的,继续往下看
>>> x = [1, 2, 1, 2, 1, 1, 1, 1] >>> for item in x: if item == 1: x.remove(item)
>>> x [2, 2, 1, 1]
同样的代码,仅仅是要处理的列表发生了一点点变化,结果并没有删除所有的1,问题出在哪里呢?粗略一想,列表的remove()方法是删除参数的第一次出现,无法指定下标位置,是这个问题吗?看看下面的代码就知道了
>>> x = [1, 2, 1, 2, 1, 1, 1, 1] >>> for i in range(len(x)): if x[i] == 1: del x[i]
Traceback (most recent call last): File "<pyshell#170>", line 2, in <module> if x[i] == 1: IndexError: list index out of range >>> x [2, 2, 1, 1]
不但没有解决问题,反而引发了一个异常,下标越界。但这个异常似乎揭示了问题所在。下标越界,为什么会下标越界呢?难道是删除了列表中的元素导致元素个数减少并且影响了后续元素的索引?
>>> x = [1, 2, 1, 2, 1, 1, 1, 1] >>> for i in range(len(x)): print(i, x) if x[i] == 1: del x[i]
0 [1, 2, 1, 2, 1, 1, 1, 1] 1 [2, 1, 2, 1, 1, 1, 1] 2 [2, 2, 1, 1, 1, 1] 3 [2, 2, 1, 1, 1] 4 [2, 2, 1, 1] Traceback (most recent call last): File "<pyshell#177>", line 3, in <module> if x[i] == 1: IndexError: list index out of range
好像真的是这个问题,为了更好地理解这个问题,看下面的代码
>>> x = [(0,1),(1,1),(2,1),(3,1),(4,1),(5,1)] >>> for i in range(len(x)): print(i, x) if x[i][1] == 1: del x[i]
0 [(0, 1), (1, 1), (2, 1), (3, 1), (4, 1), (5, 1)] 1 [(1, 1), (2, 1), (3, 1), (4, 1), (5, 1)] 2 [(1, 1), (3, 1), (4, 1), (5, 1)] 3 [(1, 1), (3, 1), (5, 1)] Traceback (most recent call last): File "<pyshell#183>", line 3, in <module> if x[i][1] == 1: IndexError: list index out of range >>> x [(1, 1), (3, 1), (5, 1)]
好了,问题的原因已经确定了,那么正确的代码该怎么写呢?既然从列表中间位置删除元素会导致后面的元素索引发生改变,那么就从后往前删除好了。
>>> x = [1, 2, 1, 2, 1, 1, 1, 1] >>> for i in range(len(x)-1, -1, -1): print(i, x) if x[i] == 1: del x[i]
7 [1, 2, 1, 2, 1, 1, 1, 1] 6 [1, 2, 1, 2, 1, 1, 1] 5 [1, 2, 1, 2, 1, 1] 4 [1, 2, 1, 2, 1] 3 [1, 2, 1, 2] 2 [1, 2, 1, 2] 1 [1, 2, 2] 0 [1, 2, 2] >>> x [2, 2]