从列表中删除所有出现的特定值?

533

在Python中,remove()函数会从列表中移除第一个出现的值。

如何从列表中删除所有出现的值?

这是我想到的方法:

>>> remove_values_from_list([1, 2, 3, 4, 2, 2, 3], 2)
[1, 3, 4, 3]
26个回答

696

函数式编程方法:

Python 3.x

>>> x = [1,2,3,2,2,2,3,4]
>>> list(filter((2).__ne__, x))
[1, 3, 3, 4]
或者
>>> x = [1,2,3,2,2,2,3,4]
>>> list(filter(lambda a: a != 2, x))
[1, 3, 3, 4]
或者
>>> [i for i in x if i != 2]
Python 2.x
>>> x = [1,2,3,2,2,2,3,4]
>>> filter(lambda a: a != 2, x)
[1, 3, 3, 4]

161
使用列表推导式取代filter+lambda,前者更易读且通常更高效。 - habnabit
21
s/通常/通常是/ - habnabit
133
habnabit的建议所对应的代码是:[y for y in x if y != 2] - coredumperror
14
我不会把这个解决方案称为最好的。列表推导式在快速浏览代码时更快、更易于理解。这更像是 Perl 的方式,而不是 Python 的。 - Peter Nimroot
11
直接调用__ne__会得到-1。将两个值进行比较远比只在其中一个上调用__eq____ne__复杂得多。尽管在这里可能因为你只比较数字而正确工作,但在一般情况下却是错误的并且是个bug。 - Aran-Fey
显示剩余3条评论

284

你可以使用列表推导式:

def remove_values_from_list(the_list, val):
   return [value for value in the_list if value != val]

x = [1, 2, 3, 4, 2, 2, 3]
x = remove_values_from_list(x, 2)
print x
# [1, 3, 4, 3]

9
你如何在不检查物品的情况下移除它们? - Alexander Ljungberg
24
这不会修改原始列表,而是返回一个新的列表。 - John Y
9
@Selinap回复道:不,这样是最优的,因为它只扫描一次列表。在你原来的代码中,无论是in运算符还是remove方法都会扫描整个列表(直到找到匹配项),因此你最终会以这种方式多次扫描列表。 - John Kugelman
6
@mhawke,@John Y:只需使用x[:] = ...而不是x =,它将是“原地”而不仅仅是重新绑定名称“x”(速度基本相同,并且比x.remove要快得多!!!)。 - Alex Martelli
18
我投票支持这个观点,因为经过6年的Python编程后,我仍然不理解Lambda表达式 :) - neydroydrec
显示剩余7条评论

145

如果原始列表必须被修改,你可以使用切片赋值,同时仍然使用高效的列表推导或生成器表达式。

>>> x = [1, 2, 3, 4, 2, 2, 3]
>>> x[:] = (value for value in x if value != 2)
>>> x
[1, 3, 4, 3]

1
@Selinap:filter函数不会修改原列表,它会返回一个新的列表。 - E.M.
过滤器和列表推导式不会修改列表。切片赋值会修改列表,而原始示例也会修改列表。 - A. Coady
18
我喜欢这个操作,因为它修改了x所引用的列表。如果还有其他引用该列表的地方,它们也会受到影响。与“x = [v for v in x if x!= 2]”方案不同,该方案创建一个新的列表并将x更改为引用它,而原始列表保持不变。 - Hannes

53

以更抽象的方式重复第一篇帖子的解决方案:

>>> x = [1, 2, 3, 4, 2, 2, 3]
>>> while 2 in x: x.remove(2)
>>> x
[1, 3, 4, 3]

40
尽管时间复杂度为O(n*n)。 - Hannes
@Hannes,难道不应该是O(n)吗?因为它只遍历一次循环并同时删除该项。 - penta
5
考虑 x = [1] * 10000 + [2] * 1000。循环体执行1000次,并且每次调用 .remove() 都要跳过10000个元素。这似乎是 O(n*n) 的,但没有证据。我认为证明的方法是假设列表中数字2的数量与其长度成比例。这个比例因子在大O符号中消失了。然而,如果列表中只有常数个2,则最好的情况不是 O(n^2),而只是O(2n),它是O(n)。 - Hannes

41

看看这个简单的解决方案

>>> [i for i in x if i != 2]

这将返回一个列表,其中包含所有没有2x元素。


1
如果列表非常长,由于需要遍历所有项,可能需要一分钟才能完成。 - Milad shiri

24
更好的解决方案是使用列表推导式。
x = [i for i in x if i!=2]

15
除了Martin Andersson之外的所有答案都是创建一个新列表,而不是从原始列表中删除所需项目。
>>> import random, timeit
>>> a = list(range(5)) * 1000
>>> random.shuffle(a)

>>> b = a
>>> print(b is a)
True

>>> b = [x for x in b if x != 0]
>>> print(b is a)
False
>>> b.count(0)
0
>>> a.count(0)
1000

>>> b = a
>>> b = filter(lambda a: a != 2, x)
>>> print(b is a)
False

如果您有其他对列表的引用,这可能很重要。

要在原地修改列表,请使用以下方法

>>> def removeall_inplace(x, l):
...     for _ in xrange(l.count(x)):
...         l.remove(x)
...
>>> removeall_inplace(0, b)
>>> b is a
True
>>> a.count(0)
0

就速度而言,在我的笔记本电脑上进行的测试结果如下(所有测试都在一个5000个条目列表中进行,其中1000个条目已被删除):
  • 列表推导式 - 约400微秒
  • 过滤器 - 约900微秒
  • .remove()循环 - 50毫秒
所以.remove()循环大约慢100倍……嗯,也许需要一种不同的方法。我找到的最快的方法是使用列表推导式,然后替换原始列表的内容。
>>> def removeall_replace(x, l):
....    t = [y for y in l if y != x]
....    del l[:]
....    l.extend(t)
  • removeall_replace() - 450us

为什么不直接将新列表重新分配到旧地址下呢?def remove_all(x, l): return [y for y in l if y != x] 然后 l = remove_all(3,l) - Dannid
@Dannid 这是第一个代码框中的第二种方法。它创建了一个新列表,而不是修改旧列表。任何对该列表的其他引用都将保持未过滤状态。 - Paul S
啊,对了。我太专注于定义一个方法了,忽略了你已经做的简单赋值。 - Dannid

6

Numpy方法对比一个包含1,000,000个元素的列表/数组:

时间:

In [10]: a.shape
Out[10]: (1000000,)

In [13]: len(lst)
Out[13]: 1000000

In [18]: %timeit a[a != 2]
100 loops, best of 3: 2.94 ms per loop

In [19]: %timeit [x for x in lst if x != 2]
10 loops, best of 3: 79.7 ms per loop

结论:在我的笔记本电脑上,numpy相对于列表推导式方法快27倍。

附注:如果你想将你的常规Python列表 lst 转换为numpy数组:

arr = np.array(lst)

设置:

import numpy as np
a = np.random.randint(0, 1000, 10**6)

In [10]: a.shape
Out[10]: (1000000,)

In [12]: lst = a.tolist()

In [13]: len(lst)
Out[13]: 1000000

检查:

In [14]: a[a != 2].shape
Out[14]: (998949,)

In [15]: len([x for x in lst if x != 2])
Out[15]: 998949

5
a = [1, 2, 2, 3, 1]
to_remove = 1
a = [i for i in a if i != to_remove]
print(a)

也许不是最pythonic的,但对我来说仍然是最简单的哈哈。

5

要删除所有重复的出现并在列表中留下一个:

test = [1, 1, 2, 3]

newlist = list(set(test))

print newlist

[1, 2, 3]

这是我在欧拉计划中使用的函数:

def removeOccurrences(e):
  return list(set(e))

2
我需要在一个包含250k个值的向量上执行此操作,它完美地运行。 - rschwieb
1
答案是:是的!如果一个有能力的程序员听到这么长的向量,感到完全疯狂,我完全理解。我作为数学家来解决问题,不担心优化解决方案,这可能导致比标准解决方案更长的解决方案。(尽管我对超过5分钟的解决方案没有耐心。) - rschwieb
7
这将从列表中移除任何排序。 - asmeurer
4
也许是因为它没有回答当前问题,而是一个完全不同的问题。 - drevicko
8
这不是对OP问题的回答,而是一个去重的解决方案,这是两个完全不同的问题。 - Anoyz
显示剩余2条评论

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