如何在不使用"|"的情况下将两个集合连接成一行

218
假设已经有了集合ST。如果不使用连接运算符|,那么如何找到这两个集合的并集呢?例如,下面的代码可以找到它们的交集:
S = {1, 2, 3, 4}
T = {3, 4, 5, 6}
S_intersect_T = { i for i in S if i in T }

那么如何在不使用|的情况下一行代码找到两个集合的并集呢?


1
你需要进行合并吗?如果是的话,你可以使用 s.union(t)。 - Ansuman Bebarta
93
为什么不能使用符号 | - Scott Bartell
4
有没有任何通用的理由不使用竖杠(|)? - matanster
7
可能的一个原因是将集合操作作为函数参数传递。想象一个函数,类似于:def apply_set_operation(a, b, set_operation)。在调用这个函数时,我更喜欢使用apply_set_operation(a, b, set.union)而不是apply_set_operation(a, b, set.__or__) - bsa
一个抽象集合操作的函数有什么用途?为什么不直接使用 a | b 而要调用函数来实现呢? - rodrigo-silveira
9个回答

372
你可以使用union方法处理集合: set.union(other_set) 请注意,它返回一个新的集合,即它不会修改自身。

74
然而,符号“|”可以内联修改变量:set_a |= set_b - jorgenkg
14
与 @jorgenkg 相同:set_a = set_a.union(set_b)。如果您的意思是“原地修改”,那么两者都不会这样做,它们都会创建一个新的 set - nitely
3
它仍然会创建一个新集合并替换引用。 - Alvaro
3
根据这个简单的测试:a = set((1, 2, 3,)); b = set((1, 3, 4,)); id_a = id(a); a |= b; assert id_a == id(a),@jorgenkg 是正确的 - 变量 a 在内部被修改了。我有遗漏什么吗? 注:该段文字已经是中文,无需翻译。 - johndodo
4
不是的,看起来不是这样:a = set((1, 2, 3,)); b = set((1, 3, 4,)); c = a; a |= b; assert id(c) == id(a). 即使 a 被销毁了,c 也不会被销毁。此外,现在 cset([1, 2, 3, 4]),所以 @jorgenkg 的评论是正确的。 - johndodo
显示剩余4条评论

62

您可以使用or_别名:

>>> from operator import or_
>>> from functools import reduce # python3 required
>>> reduce(or_, [{1, 2, 3, 4}, {3, 4, 5, 6}])
set([1, 2, 3, 4, 5, 6])

11
喜欢这种方法,更加实用,可应用于两个或更多的集合。 - Colin Su

57

如果您愿意修改原始集合(在某些情况下可能需要这样做),可以使用set.update()

S.update(T)

该函数的返回值为 None,但会更新变量 S 的值,使其成为原始 ST 的并集。


30
假设你也无法使用等同于 s | ts.union(t),你可以尝试:
>>> from itertools import chain
>>> set(chain(s,t))
set([1, 2, 3, 4, 5, 6])

或者,如果您想要更好的理解,
>>> {i for j in (s,t) for i in j}
set([1, 2, 3, 4, 5, 6])

23

你可以像这样将两个集合解包成一个:

>>> set_1 = {1, 2, 3, 4}
>>> set_2 = {3, 4, 5, 6}
>>> union = {*set_1, *set_2}
>>> union
{1, 2, 3, 4, 5, 6}
* 将集合解压缩。 解压缩是指将可迭代对象(例如集合或列表)表示为其产生的每个项。 这意味着上面的示例简化为{1, 2, 3, 4, 3, 4, 5, 6},然后简化为{1, 2, 3, 4, 5, 6},因为set只能包含唯一的项。

2
第三行的 * 是什么作用? - altabq
@altabq在回答中解释了星号表达式是什么。您也可以在REPL中尝试使用它。看看print(set_1)print(*set_1)的区别。此外,这个链接可能会给您更多信息:https://dev59.com/Ymcs5IYBdhLWcg3w0HOz - Aaron Bell
感谢@aaron-bell,我发表评论后,答案已经被编辑以包含解释。 - altabq

18
如果您的“join”指的是“union”,请尝试以下方法:
set(list(s) + list(t))

这可能有点取巧,但我想不到更好的一行代码来实现它。


如果你执行一个并集,set(list(s) + list(t))将会给你相同的结果。 - Ansuman Bebarta
我知道,但看起来他似乎试图避免使用Python内置函数,否则他就会直接使用|运算符了。 - BenjaminCohen
listset是Python内置函数。 - whackamadoodle3000

14

假设你有两个列表

 A = [1,2,3,4]
 B = [3,4,5,6]

因此,您可以按照以下方式找到A并集B

 union = set(A).union(set(B))

如果您想找到交集和非交集,则可以按照以下方式进行操作。

 intersection = set(A).intersection(set(B))
 non_intersection = union - intersection

6
您可以使用union或简单的列表推导来实现。
[A.add(_) for _ in B]

一个A应该拥有B的所有元素。

2
使用列表推导式来产生副作用并且带有匿名参数是非常糟糕的做法。 - Jean-François Fabre

4
如果您想加入`n`个集合,最佳性能似乎来自于`set().union(*list_of_sets)`,它将返回一个新的集合。
因此,使用方法可能如下:
s1 = {1, 2, 3}
s2 = {2, 3, 4}
s3 = {4, 5, 6}

s1.union(s2, s3) # returns a new set
# Out: {1, 2, 3, 4, 5, 6}
s1.update(s2, s3) # updates inplace

Alexander Klimenko 上面的回答中,我做了一些简单的测试,如下所示。我相信主要结论是似乎集合越随机,性能差异就越大。

from random import randint

n = 100

generate_equal = lambda: set(range(10_000))
generate_random = lambda: {randint(0, 100_000) for _ in range(10_000)}

for l in [
    [generate_equal() for _ in range(n)],
    [generate_random() for _ in range(n)]
]:
    %timeit set().union(*l)
    %timeit reduce(or_, l)

Out:
  # equal sets: 69.5 / 23.6 =~ 3
  23.6 ms ± 658 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
  69.5 ms ± 2.57 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
  # random sets: 438 / 78.7 =~ 5.6
  78.7 ms ± 1.48 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
  438 ms ± 20.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

因此,如果您想进行原地更新操作,最佳性能来自于set.update方法,因为在性能方面,s1.update(s2, s3) = set().union(s2, s3)

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