標籤:

Python:為什麼下面這段程序只刪除1個0?

s=[0,0,5,3]

for x in s:

if x==0

s.remove(x)

結果為什麼是[0,5,3]而不是[5,3]呢?


初始s[0] = 0, s[1] = 0, s[2] = 5, s[3] = 3

for執行第一次的時候,

x = s[0], 也就是 x = 0 然後將這個元素剔除,

s變成了[0, 5, 3], 也就是 s[0] = 0, s[1] = 5, s[2] = 3

for 執行第二次的時候,

x = s[1], 也就是 x = 5

因為x不是0,s不變

造成出現你這個結果的原因是列表s在運算過程中發生了變化.

合理的方法是將不是0的的元素重新放到一個新列表裡面

print [x for x in s if x != 0]


洛克正解,不過手癢,也來分享一下我的看法吧

在for x in s:中的s是你迭代的列表, 而在迭代中, 你又使用了s.remove(x), 這一行代碼修改了原來的列表s, 這樣, 第二次執行"for x in s:"的時候, 這裡的s和第一次執行的已經不是同一個列表了, 第一次s = [0, 0, 5, 3], 第二次s = [0, 5, 3], 但是, 他們使用的下標卻又是同一組的下標, 即第一次下標為0, 第二次下標為1, 那麼, 對應兩次的列表s, 可知, 第一次s[0]為0, 第二次s[1]為2(對應第二個s)

所以, 這個很容易犯的錯誤, 就是因為迭代的列表發生了變化, 因此在python的迭代中, 一定不要修改列表(這裡提一個你以後會犯的錯誤--列表的可變性和多引用, 導致修改其他列表時會改變原列表的問題)

那麼, 除了洛克的列表推倒式(python中最好用, 最具特色, 最X炸天的技巧, 你必須要會哦), 其他解法:

1.)新建一個列表result = [], 保存符合條件的值result.append(x), 這樣就沒有修改原列表了

2.)使用a = s, 然後迭代a, for x in a:, 再使用s.remove(x)修改列表s, 那恭喜你, 你錯了, 就是上面提到的多引用問題, 這個自己去好好看文檔弄明白吧

最後如果看不太明白, 那分享一句話"書讀百遍, 其義自見"


@洛克 的解釋很正確,但是方法可以改進。

這其實更適合通過 Python 的 filter 來解決:

#!/usr/bin/env python3

def check_number(x):
if x == 0:
return False
else:
return True

s = [0,0,5,3]

print(filter(check_number,s))


不要在循環中對數組進行變更操作,重要的事情我也不願意說三次了。


循環時盡量不要對list進行增,刪操作


#手機碼字,對付看吧

官方文檔里有很好的例子,就在2.7版本的list章節。

循環時不實用原列單a[],而是使用a[:]

這個方法最簡單,也不需要增加額外的代碼。

另外,@黑溜兒 的問題很好,@洛克 的解釋是對的。因為for循環的時候不是先獲得列單長度,這樣效率太低了。for是通過iter迭代器實現的,所以是動態的按下標迭代直到結尾。

Life is short, I use python.


s已經不是原來的s,x確實原來的x


這樣解釋的話,循環體會執行四次,那第四次應該是數組越界了,為毛不報錯?python是怎麼個檢查機制?


前兩天編程的時候也遇到了同樣的問題,題目是這樣的:給定一個字元串刪除兩邊的空格。

我的做法是將字元串轉換為list,然後迭代的去刪除list中的空格。

list1 = list(str1)

for i in list1:

if i == and (list1.index(i) == 0 or list1.index(i) == len(list1)-1):

list.remove(i)

在開始的時候開頭或結尾只存在一個空格這個方法是好用的.但是當開頭存在兩個空格的時候就不好用了,怎麼刪都刪不掉。原來是這個原因。明白了


之前在工作里也遇到了這個問題,樓上各位都說的很不錯。補充一點,字典其實也有類似的問題。不同的是,對於字典的迭代似乎會在每一次迭代結束之後檢查字典是不是變化了,如果變化會報錯。


s = [i for i in s if i != 0]


s = [0,0,5,3]
print(filter(lambda x: x != 0, s))


推薦閱讀:

TAG:Python |