在 Python 中不使用特定元素返回 list 的快速方法

如果我有一张纸牌套装的清单,按照如下任意顺序排列:

suits = ["h", "c", "d", "s"]

我想返回一个没有 'c'的列表

noclubs = ["h", "d", "s"]

有什么简单的方法吗?

129035 次浏览
suits = ["h","c", "d", "s"]


noclubs = [x for x in suits if x != "c"]
>>> suits = ["h","c", "d", "s"]
>>> noclubs = list(suits)
>>> noclubs.remove("c")
>>> noclubs
['h', 'd', 's']

如果你不需要一个单独的 noclubs

>>> suits = ["h","c", "d", "s"]
>>> suits.remove("c")

如果删除 具体点元素很重要(而不仅仅是过滤) ,那么您需要接近以下内容:

noclubs = [x for i, x in enumerate(suits) if i != suits.index('c')]

如果您的问题确实与扑克牌有关,您也可以考虑在这里使用 set,以便在语义上更正确。

如果命令 没有事项,可以使用设置操作:

suits = ["h", "c", "d", "s"]
noclubs = list(set(suits) - set(["c"]))
# note no order guarantee, the following is the result here:
# noclubs -> ['h', 's', 'd']

您可以使用 filter (或者来自 itertools 的 filter)

suits = ["h","c", "d", "s"]
noclubs = filter(lambda i: i!='c', suits)

您也可以使用列表构造进行筛选

suits = ["h","c", "d", "s"]
noclubs = [ i for i in suits if i!='c' ]

这个问题已经得到了回答,但是我还是想说明一下,使用列表内涵比使用 .remove()慢得多。

我机器上的一些概要文件(笔记本使用 Python 3.6.9)。

x = ['a', 'b', 'c', 'd']


%%timeit
y = x[:]  # fastest way to copy
y.remove('c')


1000000 loops, best of 3: 203 ns per loop


%%timeit
y = list(x)  # not as fast copy
y.remove('c')


1000000 loops, best of 3: 274 ns per loop


%%timeit
y = [n for n in x if n != 'c']  # list comprehension


1000000 loops, best of 3: 362 ns per loop


%%timeit
i = x.index('c')
y = x[:i] + x[i + 1:]


1000000 loops, best of 3: 375 ns per loop

如果你用最快的方法复制一个列表(这不是很容易读) ,你会比使用列表内涵快45% 。但是如果你通过使用 list()类(更常见的 Python 类)来复制列表,那么你会比使用列表内涵慢25% 。

真的,一切都发生得太快了。我认为可以这样说,abc0比列表内涵技术更具可读性,但它并不一定更快,除非你有兴趣放弃复制的可读性。

在这种情况下,列表内涵的最大优势在于它更加简洁(例如,如果你有一个函数,出于某种原因要从一个给定的列表中删除一个元素,它可以在一行中完成,而另一个方法需要3行)有时候一行程序非常方便(尽管它们通常以牺牲一些可读性为代价)。此外,在不知道要删除的元素是否真的在列表中的情况下,使用列表内涵 Excel。虽然 abc0会抛出一个 ValueError,列表内涵将按照预期运行。

不使用 for 循环或 lambda 函数并保持顺序:

suits = ["h","c", "d", "s"]
noclubs = suits[:suits.index("c")]+suits[suits.index("c")+1:]

我知道它在内部仍然使用循环,但至少您不必在外部使用它们。

不幸的是,在缺省情况下,Python 中似乎没有内置任何类似的东西。

有几个答案,但我想我会添加一个使用迭代器。如果在适当的地方改变是可以接受的,那将是最快的。如果你不想改变原始的,只是想循环一个过滤集,这应该是相当快的:

实施方法:

def without(iterable, remove_indices):
"""
Returns an iterable for a collection or iterable, which returns all items except the specified indices.
"""
if not hasattr(remove_indices, '__iter__'):
remove_indices = {remove_indices}
else:
remove_indices = set(remove_indices)
for k, item in enumerate(iterable):
if k in remove_indices:
continue
yield item

用法:

li = list(range(5))
without(li, 3)
# <generator object without at 0x7f6343b7c150>
list(without(li, (0, 2)))
# [1, 3, 4]
list(without(li, 3))
# [0, 1, 2, 4]

因此,它是一个发电机-你需要调用 list或其他东西,使其永久性。

如果您只想删除单个索引,当然可以使用 k == remove_index而不是 set 使其更快。

一种可能性是使用 filter:

>>> import operator
>>> import functools


>>> suits = ["h", "c", "d", "s"]


>>> # Python 3.x
>>> list(filter(functools.partial(operator.ne, 'c'), suits))
['h', 'd', 's']


>>> # Python 2.x
>>> filter(functools.partial(operator.ne, 'c'), suits)
['h', 'd', 's']

在这里,我们也可以使用 'c'__ne__方法来代替 partial:

>>> list(filter('c'.__ne__, suits))
['h', 'd', 's']

然而,后一种方法并不被认为是非常 Python 化的(通常你不应该直接使用特殊的方法——从双下划线开始) ,如果列表中包含混合类型,那么 可以会给出奇怪的结果,但是它可能比 partial方法快一点。

suits = ["h", "c", "d", "s"]*200   # more elements for more stable timings
%timeit list(filter('c'.__ne__, suits))
# 164 µs ± 5.98 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit list(filter(functools.partial(operator.ne, 'c'), suits))
# 337 µs ± 13.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit list(filter(lambda x: x != 'c', suits))
# 410 µs ± 13.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit [x for x in suits if x != "c"]
181 µs ± 465 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)

使用 IPythons magic %timeit命令测试 Python 3.5.2。