python循環中慎用pop()函數和remove()函數

本文以列表作爲示例,講述爲什麼在循環體中不能使用pop()函數和remove()函數。

出現的問題

一般地,刪除列表中的一個元素有pop()remove()方法。例如下面的例子:

lst = [1, 2, 3, 4 ,5]
print(lst)
lst.pop()
print(lst)
lst.remove(1)
print(lst)

結果爲

[1, 2, 3, 4, 5]
[1, 2, 3, 4]
[2, 3, 4]

但是在循環體中不能怎麼做,列表循環一般有兩種方式,一種是for i in range(len(lst)),另外一種是for item in lst

  • 第一種循環
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]
1
4
5
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-25-7e590a65a8c9> in <module>
      3 
      4 for i in range(len(num_list)):
----> 5     if num_list[i] == 2:
      6         num_list.pop(i)
      7     else:

IndexError: list index out of range

可以看到程序報錯,出錯的原因是當刪除了列表中的元素2時,num_list的長度變爲了4,所以在循環到最後一個元素的時候,會提示下標超出範圍。

  • 第二種循環
num_list = [1, 2, 3, 4, 5]
print(num_list)

for item in num_list:
    if item == 2:
        num_list.remove(item)
    else:
        print(item)

print(num_list)

輸出結果爲

[1, 2, 3, 4, 5]
1
4
5
[1, 3, 4, 5]

奇怪的發現,元素3怎麼丟失了?其實這和python循環中的迭代器有關,當迭代器到達num_list中的第二個元素2的時候,將其刪除,然後迭代器將會自動的指向列表的下一個元素,由於這時候已經刪除了第二個元素,所以迭代器便指向了新數組的第3個元素也就是4,所以出現了上述的情況,下面的圖可以比較清晰的解釋上面的過程。
在這裏插入圖片描述


仔細觀察可以發現,雖然上述的結果中間打印的時候漏掉了一個元素,對最終的結果沒有什麼影響,但是這只是一種比較幸運的情況,比如下面的例子就沒那麼幸運了。

x = ['a', 'b', 'c', 'd']
y = ['b', 'c']
for i in x:
    if i in y:
        x.remove(i)
print(x)

輸出結果爲

['a', 'c', 'd']

按照正常的理解,應該最終的結果爲['a', 'd'],但是最終結果卻多了一個'c',我們根據上述的迭代圖進行分析,當遍歷到列表x'b'元素時,判斷條件成立,刪除列表x中的'b'元素,這是列表x=['a', 'c', 'd'],但此時的迭代器位於列表的第三個元素,也就是新列表x'd'元素,直接把'c'忽略掉了,所以最終結果纔會出現'c'


解決方法

要想解決這個問題,一種方法自然是定義一個新的數組,將結果存到新數組中,或者從後面往前面遍歷,這時候也可以避免上述漏掉元素的情況。

  • 方法1:定義一個新的列表
x = ['a', 'b', 'c', 'd']
y = ['b', 'c']
new_list = list()
for i in x:
    if i in y:
        x.remove(i)
    else:
        new_list.append(i)
print(new_list)
  • 方法2:從後面往前遍歷列表
x = ['a', 'b', 'c', 'd']
y = ['b', 'c']
for i in range(len(x) - 1, -1, -1):
    if x[i] in y:
        x.pop(i)
print(x)

兩種方法的輸出結果都一樣

['a', 'd']

不過還是建議使用第一種方法,雖然更佔內存,但是簡潔易懂,也容易對新列表進行操作。

參考

https://www.cnblogs.com/bananaplan/p/remove-listitem-while-iterating.html
https://kyle.ai/blog/6565.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章