在遍歷list的時候,刪除符合條件的數據,結果不符合預期
num_list = [1, 2, 2, 2, 3]
print(num_list)
for item in num_list:
if item == 2:
num_list.remove(item)
else:
print(item)
print(num_list)
結果是
[1, 2, 2, 2, 3]
1
[1, 2, 3]
或者有:
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] Traceback (most recent call last): 1
4
5 File "tes.py", line 5, in <module>
if num_list[i] == 2: IndexError: list index out of range Process finished with exit code 1
原因是,刪除list中的元素后,list的實際長度變小了,但是循環次數沒有減少,依然按照原來list的長度進行遍歷,所以會造成索引溢出。
1.把列表拷貝,然后對原列表進行刪除操作就沒問題了
num_list = [1, 2, 2, 2, 3] print(num_list) for item in num_list[:]: if item == 2: num_list.remove(item) else: print(item) print(num_list)
結果:
[1, 2, 2, 2, 3]
1
3
[1, 3]
num_list[:]是對原始的num_list的一個拷貝,是一個新的list,所以,我們遍歷新的list,而刪除原始的list中的元素,則既不會引起索引溢出,最后又能夠得到想要的最終結果。此方法的缺點可能是,對於過大的list,拷貝后可能很占內存。
2.從后往前遍歷列表,刪除
3.filter函數
例子 list中去空字符(配合lambda表達式):
condition = lambda t: t != "" (判斷符合條件很復雜就不能使用lambda,自己寫方法吧)
filter_list = list(filter(condition, list)
Python的List的底層是實現是一個PyObject*數組。如果每次增加一個元素都擴張內存的話效率太低,在增加元素的時候所以會有預申請內存。同理刪除元素的時候也不是馬上就減小內存空間,他會按照一定的策略減小。而這種減小不太好預測。
再加上刪除后索引如何調整並沒有明確定義。比如一個長度為3的List你刪除了第2個那么第三個下標應該是2還是3,等等可能會引發歧義。如果這個時候內存達到了需要減小的條件,迭代器就不太好判斷新的索引究竟是多少。
所以Python為了通用性和安全性考慮就禁止在遍歷時刪除元素了。
比較容易的是用內置的set
l1 = ['b','c','d','b','c','a','a']
l2 = list(set(l1))
print l2
還有一種據說速度更快的,沒測試過兩者的速度差別
l1 = ['b','c','d','b','c','a','a']
l2 = {}.fromkeys(l1).keys()
print l2
這兩種都有個缺點,祛除重復元素后排序變了:
['a', 'c', 'b', 'd']
如果想要保持他們原來的排序:
用list類的sort方法
l1 = ['b','c','d','b','c','a','a']
l2 = list(set(l1))
l2.sort(key=l1.index)
print l2
也可以這樣寫
l1 = ['b','c','d','b','c','a','a']
l2 = sorted(set(l1),key=l1.index)
print l2
也可以用遍歷
l1 = ['b','c','d','b','c','a','a']
l2 = []
for i in l1:
if not i in l2:
l2.append(i)
print l2
上面的代碼也可以這樣寫
l1 = ['b','c','d','b','c','a','a']
l2 = []
[l2.append(i) for i in l1 if not i in l2]
print l2
這樣就可以保證排序不變了:
['b', 'c', 'd', 'a']