List和Set之间的差异运算符

时间:2016-03-26 19:06:32

标签: python python-3.x

是否有运营商根据List的内容从Set删除元素?

这样做我已经可以做到了:

words = ["hello", "you", "how", "are", "you", "today", "hello"]
my_set = {"you", "are"}

new_list = [w for w in words if w not in my_set]
# ["hello", "how", "today", "hello"]

令我困扰的是这个列表理解是,对于大型集合,它看起来不如可以在两个集合之间使用的-运算符有效。因为在列表推导中,迭代发生在Python中,而对于运算符,迭代发生在C并且更低级,因此更快。

因此,有一种方法可以比使用列表推导更短/更清晰/更有效地计算List和Set之间的差异,例如:

# I know this is not possible, but does something approaching exist?
new_list = words - my_set

TL; DR

我正在寻找一种方法从Set移除List中的所有元素呈现,即:

  • 清洁工(可能内置)
  • 和/或更高效

比我所知道的可以用列表推导完成。

3 个答案:

答案 0 :(得分:4)

不幸的是,唯一的答案是:不,没有内置的方式,在本机代码中实现,用于这种操作。

  

令我困扰的是这个列表理解是,对于大型集合,它看起来不如可以在两个集合之间使用的-运算符有效。

我认为这里重要的是“外观”部分。是的,列表推导在Python中比在集合差异中运行更多,但我假设您的大多数应用程序实际上都在Python中运行(否则您可能应该使用C编程)。所以你应该考虑它是否真的很重要。在Python中迭代列表很快,并且对集合的成员资格测试也非常快(恒定时间,并在本机代码中实现)。如果你看一下列表理解,它们也非常快。所以它可能没那么重要。

  

因为在列表理解中,迭代发生在Python中,而对于运算符,迭代发生在C中并且更低级,因此更快。

本机操作确实更快,但它们也更专业,更有限,并且灵活性更低。对于套装而言,差异非常简单。 set difference是一个数学概念,定义非常明确。

但是当谈到“列表差异”或“列表和设置差异”(或更广义的“列表和可迭代差异”?)时,它变得更加不清楚。有很多悬而未决的问题:

  • 如何处理重复项?如果原始列表中有两个X且减数中只有一个X,那么X是否应该从列表中消失?应该只有一个消失吗?如果是这样,哪一个,以及为什么?

  • 如何处理订单?订单应该保留在原始列表中吗?减数中元素的顺序是否有影响?

  • 如果我们想根据平等以外的其他条件减去成员,该怎么办?对于集合,很明显它们总是处理成员的相等(和哈希值)。列表没有,因此列表按设计更灵活。使用列表推导,我们可以轻松地从列表中删除任何条件;如果您考虑到这一点,我们将被限制在“平等差异”中,这可能实际上是一种罕见的情况。

    如果您需要计算差异(甚至某些ordered set),可能更有可能使用集合。对于过滤列表,您可能也希望最终得到过滤的列表,因此使用生成器表达式(或Python 3 {{1})可能更常见。稍后使用它而不必在内存中创建过滤后的列表。

我想说的是列表差异的用例并不像设定差异那样清晰。如果有一个用例,它可能是一个非常罕见的用例。总的来说,我不认为为此增加Python实现的复杂性是值得的。特别是在Python中的替代方案,例如列表理解,就像现在一样快。

答案 1 :(得分:1)

首先,您是否过早地担心优化问题并不是真正的问题?我必须要有至少10,000,000个元素的列表才能进入此操作的范围,需要1/10秒。

如果您正在处理大型数据集,那么您可能会发现转向使用numpy是有利的。

import random
import timeit

r = range(10000000)

setup = """
import numpy as np
l = list({!r})
s = set(l)
to_remove = {!r}
n = np.array(l)
n_remove = np.array(list(to_remove))
""".format(r, set(random.sample(r, 3)))

list_filter = "[x for x in l if x not in to_remove]"
set_filter = "s - to_remove"
np_filter = "n[np.in1d(n, n_remove, invert=True)]"

n = 1
l_time = timeit.timeit(list_filter, setup, number=n)
print("lists:", l_time)
s_time = timeit.timeit(set_filter, setup, number=n)
print("sets:", s_time)
n_time = timeit.timeit(np_filter, setup, number=n)
print("numpy:", n_time)

返回以下结果 - numpy比使用sets快一个数量级。

lists: 0.8743789765043315
sets: 0.20703006886620656
numpy: 0.06197169088128707

答案 2 :(得分:1)

我同意捅。这是我的理由:

最简单的方法是使用filter

words = ["hello", "you", "how", "are", "you", "today", "hello"]
my_set = {"you", "are"}

new_list = filter(lambda w: w not in my_set, words)

使用Dunes解决方案,我得到了这些时间:

lists:  0.87401028
sets:   0.55103887
numpy:  0.16134396
filter: 0.00000886 WOW beats numpy by various orders of magnitude !!!

但等等,我们正在进行有缺陷的比较,因为我们正在比较严格制定列表的时间(理解和设置差异)与懒惰(numpy和过滤器)。

如果我运行Dunes解决方案但生成实际列表,我会得到:

lists:  0.86804159
sets:   0.56945663  
numpy:  1.19315723
filter: 1.68792561

现在numpy比使用简单的filter稍微高效,但两者都不比列表理解更好,这是第一个也是更直观的解决方案。

我肯定会使用filter而不是理解,除非我需要多次使用已过滤的列表(尽管我可以tee)。

相关问题