是否有运营商根据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
中的所有元素呈现,即:
比我所知道的可以用列表推导完成。
答案 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
)。