Python列表邊遍歷邊修改問題解決方案:alist[:]
最近在看python,遇到個簡單的問題:刪除列表中指定的重復元素,發現一些實用並且有趣的東西。
1.錯誤示範
alist = [1,1,2,2,3,3,2,2,1,1]
for i in alist:
if i ==1:
alist.remove(1)
print(alist)
運行結果:[2, 2, 3, 3, 2, 2, 1, 1]
錯誤原因:刪除列表元素,導致列表內容改變,部分元素位置前移;當繼續進行for循環時,索引繼續加一,導致跳過一個元素。
本例中,第二個“1”和第四個“1”被跳過,但是remove()是刪除從列表第一個出現的元素,所以當第三個“1”出現進入if語句時,第二個“1”被刪除。
2.解決方法一:alist[1]
alist = [1,1,2,2,3,3,2,2,1,1]
for i in alist[:]: ###
if i ==1:
alist.remove(1)
print(alist)
運行結果:[2, 2, 3, 3, 2, 2]
簡析:將for循環中的控制語句改成" for i in alist[:]"後,程序正確實現功能。對於這個很神奇的alist[:],查了好久,都沒有找到相關資料。後來,在和朋友的討論中,我突然想到“地址”的抽象概念。錯誤實現的語句" for i in alist"中的"alist",是列表的名稱,類似於C中的數組名,其實可以看做一個指向該列表的指針,執行for循環時不斷地通過這個地址找到對應元素,導致出現錯誤結果。
然而," for i in alist[:]"中,“alist[:]”是將alist中的所有元素取出,存儲在另一塊獨立與alist的區域中,這樣的話,當修改原列表alist時,並不會修改for循環的判斷邏輯,其實就是相當於alist復制給一個新的列表,利用新的列表進行for循環的控制。
為驗證以上猜想,用id()命令分別查看alist 和 alist[:]的地址,果然不同。
3.解決方案二:定義一個新的列表
alist = [1,1,2,2,3,3,2,2,1,1]
alist_new = []
for i in alist:
if i!=1:
alist_new.append(i)
alist = alist_new
print (alist)
運行結果:[2, 2, 3, 3, 2, 2]
簡析:定義一個新的列表,將不滿足條件的值添加到新表中,遍歷原列表後,將新列表覆蓋原列表。
缺點:新列表會占用內存
4.解決方法三:改變循環條件
alist = [1,1,2,2,3,3,2,2,1,1] i = 0 while i<len(alist): if alist[i]==1: alist.pop(i) i = i-1 i = i+1 print (alist)
運行結果:[2, 2, 3, 3, 2, 2]
簡析:改變循環條件,判斷是否遍歷完成,如果刪除一個元素,後面元素前移,此時令循環條件i減1,再次判斷當前位置,正好判斷的是後移過來的新元素。直到所有元素全部遍歷。
缺點:邏輯稍復雜,比較優化
5.解決方法四:利用while循環和in、index()函數
alist = [1,1,2,2,3,3,2,2,1,1]
while 1 in alist :
alist.pop(alist.index(1))
print (alist)
運行結果:[2, 2, 3, 3, 2, 2]
簡析:(1)關鍵字in,判斷一個元素是否在一個集合中,返回True/False;同理,not in也是判斷一個元素是否不在一個集合中,返回True/False;有一個類似的關鍵字 is,判斷兩個變量是否是同一對象,即同一內存空間,或者地址相同,同樣有not is。
(2)index()方法,返回元素在序列中第一次出現的索引號,如果元素不存在於序列中,則會報出異常。
(3)pop() 函數用於移除列表中的一個元素(可用需要刪除的索引號作為輸入參數,默認刪除最後一個元素),並且返回該元素的值。
(4)邏輯:在while條件中不斷判斷列表中是否有目標元素,若有,則用index()找到該元素的索引號,並用pop()刪除,直到刪除幹凈。
6.解決方法五:利用filter()和labmbda
alist = [1,1,2,2,3,3,2,2,1,1]
alist = list(filter(lambda x: x!=1, alist))
print (alist)
運行結果:[2, 2, 3, 3, 2, 2]
簡析:(1)根據題目要求,就是對列表進行過濾處理,很自然地想到了filter()函數,配合lambda表達式簡直完美。
(2)filter()函數:用於過濾序列,過濾掉不符合條件的元素,返回由符合條件元素組成的新列表。該接收兩個參數,第一個為函數,第二個為序列,序列的每個元素作為參數傳遞給函數進行判,然後返回 True 或 False,最後將返回 True 的元素放到新列表中。
用法:filter(function, iterable)
(3)lambda:匿名定義簡單函數,主體是一個表達式,常用於函數式編程。
用法:lambda [arg1 [,arg2,.....argn]]:expression
(4)邏輯:用lambda定義匿名函數,判斷元素是否為非“1”,若真則返回True,否則返回False。在用filter函數進行過濾處理,實現對列表的過濾功能。
Python列表邊遍歷邊修改問題解決方案:alist[:]