我最近有人问我最近在迭代时更改列表的失礼。他们提出了以下场景(我现在用更好的例子更新)作为可能的用例,当时可能需要这样做:
>>> jersey_numbers = [4, 2, 3, 5, 1] # list of places in a race
>>> for jersey_number in jersey_numbers:
if jersey_number == 2: # disqualify jersey number 2 for a false start
t.remove(jersey_number)
>>> t
[4, 3, 5, 1] # jersey number 3 is now in second place
此行为是否足够常规,可用于此类用例?
答案 0 :(得分:7)
当您从列表中删除某个项目时,列表中的所有内容都会转移...
[1, 2, 3, 4, 5]
#remove ^
[1, 2, 3, 5]
如果您在迭代对象时执行此操作,并且如果您要将项目相互删除,那么当您删除第一个项目时,第二个项目将转移到它的位置。 for循环将继续递增列表中的位置,在该位置拉出一个值,使其跳过碰到的值以取代您删除的项目。
这是一个reference - 所以我们都知道这是记录良好的行为:)
答案 1 :(得分:5)
你应该使用的是:
t = filter(None, t) # or
t = [x for x in t if x]
或者如果您的情况实际上更复杂:
t = filter(lambda x: x != something, t) # or
t = [x for x in t if x != something]
顺便说一句,remove
删除了第一个匹配元素,不一定是x
当前指向的元素,尽管在您的简单案例中,行为是等效的。
当您迭代列表时,删除元素并且迭代器不知道它,让我们说你的列表是[1,0,0,2]
:
1
,没有变化,下一个0
,您删除了一些0
,这是第一个,列出更改的大小,现在迭代器指向第二个零,下一个2
,无变化实际上,您的第一个算法会删除每秒零。
你的第二个算法应该不起作用,如果你这样说,也许你没有测试它。
答案 2 :(得分:4)
错误在于您在迭代时修改列表。这不符合您的预期(当您删除当前元素时,将跳过下一个元素)。
以下是如何做到的:
In [18]: t = [5.0, 5.0, 5.0, 4.0, 0.0, 5.0, 0.0, 3.0, 5.0, 5.0, 0.0, 0.0, 4.0, 5.0, 0.0, 5.0, 4.0, 5.0, 3.0, 3.0, 5.0, 5.0, 5.0, 5.0]
In [19]: t[:] = [val for val in t if val != 0]
答案 3 :(得分:2)
您应该迭代列表的浅表副本,因为您正在修改列表,这可能会导致在迭代期间遗漏某些项目。
对于你正在做的事情,filter()
是一个不错的选择。
filter(lambda x:x != 0.0,t)
使用浅拷贝:
In [7]: t = [5.0, 5.0, 5.0, 4.0, 0.0, 5.0, 0.0, 3.0, 5.0, 5.0, 0.0, 0.0, 4.0, 5.0, 0.0, 5.0, 4.0, 5.0, 3.0, 3.0, 5.0, 5.0, 5.0, 5.0]
In [8]: for x in t[:]: # a shallow copy of t
if x==0.0:
t.remove(x)
In [9]: 0.0 in t
Out[9]: False
答案 4 :(得分:0)
t = [e for e in t if e != 0.0]
答案 5 :(得分:0)
正如@mgilson所说,当你删除了0.0
值时,第二个0.0
取代了它,并且错过了。
您可以迭代浅拷贝,并将其从原始拷贝中删除:
>>> t = [1, 2, 3, 3, 3, 4, 5]
>>> for value in t[:]:
if value == 3:
t.remove(value)
>>> t
[1, 2, 4, 5]