2011-02-10 78 views
5

我环路和删除满足我的条件的元素。但为什么不这样做,如下所述?谢谢。如何安全通过列表中删除从列表中的元素在Python

>>> a=[ i for i in range(4)] 
>>> a 
[0, 1, 2, 3] 
>>> for e in a: 
...  if (e > 1) and (e < 4): 
...   a.remove(e) 
... 
>>> a 
[0, 1, 3] 
>>> a=[ i for i in range(4)] 
>>> for e in a: 
...  if (e > -1) and (e < 3): 
...   a.remove(e) 
... 
>>> a 
[1, 3] 

回答

9

你在迭代它时不能改变某些东西。结果很奇怪,反直觉,几乎从来没有你想要的。事实上,许多收藏明确地禁止这样做(例如套和字典)。

相反,遍历一个副本(for e in a[:]: ...)或,而不是修改现有列表,过滤它获取包含您想要的物品([e for e in a if ...])一个新的列表。请注意,在很多情况下,你不必再重复进行筛选,只用数据的生成合并的过滤。

5

为什么不直接在列表理解中做到这一点?例如。

[i for i in range(4) if i <= 1 or i >= 4] 

您也可以使用它从现有列表构造一个新列表,例如,

[x for x in a if x <= 1 or x >= 4] 
+0

这将返回应该实际删除的项目。 – 2011-02-10 18:16:20

+0

@Sven对不起,我会修复它 – 2011-02-10 18:16:51

1

它是不是安全,而迭代虽然它从列表中删除元素。为此存在过滤功能。它需要一个函数(即承认一个参数)和一个可迭代(在这种情况下您的列表)。它返回相同类型的具有在施加于该元素的函数返回true的元素的新迭代(列表在这里再次):

在你的情况,你可以使用lambda函数是这样的:

a = filter(lambda x: x > 1 and x < 4, range(4)) 

或者,如果你已经列表:

a = range(4) 
a = filter(lambda x: x > 1 and x < 4, a) 

记住,如果你使用python3它会返回一个迭代器,而不是一个列表。

+0

对于这样的简单情况,我更喜欢`filter`上的列表理解。 – 2011-02-10 18:19:26

2

滤波的构思是一个很好的,但是它错过其是某些列表可能非常大和元件以去除可能是非常小的数量的点。

在这种情况下,答案是要记住的元素列表索引中移除,然后通过索引列表,排序从大到小重复,删除的元素。

+0

假设你有一个100万的元素列表和4个被删除。过滤它意味着洗牌大约100万个元素,而您的建议平均需要洗牌两次。当然,其他因素会意味着它不那么简单,但除非你确实计算了代码的时间,否则我会坚持使用最简单的(过滤),因为如果有的话,你将不会获得太多的收益复杂。 – Duncan 2011-02-10 18:56:45

2

想象它最简单的方法是考虑迭代的列表偏移,而不是实际的项目工作 - 做一些事情的第一个项目,那么第二项,再单击第三项,直到用完项目。如果更改列表中的项目的数量,它改变列表中的所有剩余项目的偏移量:

lst = [1,2,3,4] 
for item in lst: 
    if item==2: 
     lst.remove(item) 
    else: 
     print item 
print lst 

结果

1 
4 
[1,3,4] 

这是有道理的,如果你通过它一步像这样:

[1,2,3,4] 
^ 
first item is not 2, so print it -> 1 

[1,2,3,4] 
^
    second item is 2, so remove it 

[1,3,4] 
    ^
    third item is 4, so print it -> 4 

唯一真正的解决办法是当你迭代它不改变列表中的项目数量。将要保留的项目复制到新列表中,或跟踪要删除的值,并在单独的通道中执行按值删除。