迭代失败的具体行为

时间:2013-01-25 18:19:34

标签: python

我最近有人问我最近在迭代时更改列表的失礼。他们提出了以下场景(我现在用更好的例子更新)作为可能的用例,当时可能需要这样做:

>>> 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

此行为是否足够常规,可用于此类用例?

6 个答案:

答案 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]