如何在循环中从Python列表中删除元素?

66

我尝试了下面的代码来从列表中删除元素:

x = ["ok", "jj", "uy", "poooo", "fren"]
for item in x:
    if len(item) != 2:
        x.remove(item)

为什么x中的"fren"没有被移除?

5个回答

110

在迭代列表时不能删除其中的项。更简单的方法是基于旧列表构建一个新列表:

y = [s for s in x if len(s) == 2]

好吧,若要挑剔的话:你“可以”,但会得到无用的结果,而其他容器则直接禁止它。 - user395760
2
@delnan:好吧,更苛刻地说,如果你以相反的顺序迭代列表,你甚至可以得到有用的结果。但这可能不会更快(而且肯定更令人困惑),比起像Sven建议的那样创建一个新列表。 - Tim Pietzcker
是的,我是按相反的顺序执行,而且不需要复制:x = len(system_list) # 删除所有空条目 for i in range(x-1, -1, -1): if not system_list[i]: del system_list[i] - radtek
@radtek:由于从列表中删除项目是O(n)操作,因此这种方法具有O(n²)的最坏情况复杂度。 - Sven Marnach
这个答案完美地工作。 - xaratustra

49

hymloth和sven的答案是正确的,但它们并没有修改列表(它们创建了一个新列表)。如果你需要修改对象,你需要分配给一个切片:

x[:] = [value for value in x if len(value)==2]

然而,对于需要删除少量元素的大型列表,这样做会消耗大量内存,但其时间复杂度为O(n)。

glglgl的答案受到O(n²)的复杂度影响,因为list.remove是O(n)的。

根据您的数据结构不同,您可以选择记录要删除元素的索引,并使用del关键字按索引进行删除:

to_remove = [i for i, val in enumerate(x) if len(val)==2]
for index in reversed(to_remove): # start at the end to avoid recomputing offsets
    del x[index]
现在del x[i]也是O(n),因为你需要复制索引之后的所有元素(列表是一个向量),所以你需要根据你的数据测试这个。尽管如此,这应该比使用remove更快,因为你不需要为删除操作中的搜索步骤付出代价,并且在两种情况下,复制步骤的成本是相同的。

[编辑]非常好的就地、O(n)版本,具有有限的内存要求,由@Sven Marnach提供。它使用了在python 2.7中引入的itertools.compress:

from itertools import compress

selectors = (len(s) == 2 for s in x)
for i, s in enumerate(compress(x, selectors)): # enumerate elements of length 2
    x[i] = s # move found element to beginning of the list, without resizing
del x[i+1:]  # trim the end of the list

3
这里有一个仅需要O(1)的额外内存,时间复杂度为O(n)的就地(in-place)版本:http://ideone.com/F10fB。它不比你的O(n^2)版本复杂。(顺便赞一下你详细的回答) - Sven Marnach
不错。你应该在SO上发布它作为答案。 - gurney alex
为什么你会将值赋给 x[:] 而不是 x 呢? - Kirk Strauser
@KirkStrauser:你可能会对这篇帖子更多的细节感兴趣。 - Sven Marnach
@gurneyalex 感谢您展示了使用切片x[:]来避免创建新对象的用法!我来这里只是为了找这个。 - Ethan
显示剩余5条评论

7
x = [i for i in x if len(i)==2]

2
这是因为在删除时,迭代会跳过一个元素,因为它似乎只在索引上工作。
解决方法可能是:
x = ["ok", "jj", "uy", "poooo", "fren"]
for item in x[:]: # make a copy of x
    if len(item) != 2:
        print "length of %s is: %s" %(item, len(item))
        x.remove(item)

嗯...你是对的。就时间而言,据我所知,Hymloth和你的解决方案似乎是最好的... - glglgl

2

已经提到过的列表推导式方法可能是你的最佳选择。但如果你绝对想要在原地完成它(例如如果x非常大),这里有一种方法:

x = ["ok", "jj", "uy", "poooo", "fren"]
index=0
while index < len(x):
    if len(x[index]) != 2:
        print "length of %s is: %s" %(x[index], len(x[index]))
        del x[index]
        continue
    index+=1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接