Python集合(set)存放的是不重复、可哈希的元素,本身无序。它最常干两件事:给序列去重,以及做并集、交集、差集这类集合运算。因为元素要能哈希,列表、字典不能直接丢进集合,需要先转成元组等可哈希类型。

集合长什么样

非空集合可以用大括号字面量写出。打印时花括号里的顺序不必和你书写时一致,这是无序带来的正常现象:

s = {1, 2, 3}
print(s)           # {1, 2, 3}
print(type(s))     # <class 'set'>

自动去重

同一个值写进去多次,集合里最终只保留一份。把列表先交给 set 再转回 list,是去重时很省事的写法,但转回来之后的次序往往对不上原列表,若必须保序,要另想办法(例如按首次出现顺序自己筛一遍):

s = {1, 2, 2, 3, 3, 3}
print(s)   # {1, 2, 3}

nums = [1, 2, 2, 3, 3, 3, 4]
unique = list(set(nums))
print(unique)   # [1, 2, 3, 4]  次序可能不同

创建集合

从可迭代对象构造时用 set(...),字符串会按字符拆开再去重。空集合只能写 set():在 Python 语言里 {} 已经被空字典占用,这一点和非空时用花括号的写法容易打架,是新手最常写错的地方:

a = {1, 2, 3}
b = set()
c = set("hello")
print(c)               # {'h', 'e', 'l', 'o'}

empty = {}
print(type(empty))     # <class 'dict'>
empty2 = set()
print(type(empty2))    # <class 'set'>

添加与删除

add 加入单个元素。discardremove 都能删,差别在目标不存在时:前者什么也不做,后者抛 KeyErrorpop 会去掉并返回某一个元素,具体是哪一个并不固定,所以不要写依赖弹出顺序的逻辑。clear 则清空整个集合:

s = {1, 2, 3}
s.add(4)
s.discard(2)
s.remove(1)
s.pop()
print(s)
s.clear()

遍历集合

for 可以遍历当前所有元素,只是先后顺序每次运行都可能不一样。成员判断用 in,平均情况下很快,这也是集合适合做是否出现过这类检查的原因:

s = {"apple", "banana", "cherry"}
for x in s:
    print(x, end=" ")
print()

print("apple" in s)     # True

集合运算

两个集合之间可以用运算符,也可以用同名方法,结果等价。并集是两边都收进来,交集是公共部分,差集是只在左边出现的元素,对称差则是去掉公共部分后剩下的两侧私有元素:

A = {1,2,3,4} B = {3,4,5,6} A B 并集 A | B A B 交集 A & B A B 差集 A - B A B 对称差 A ^ B
并集 {1,2,3,4,5,6}、交集 {3,4}、差集 {1,2}(在 A 不在 B)、对称差 {1,2,5,6}(去掉公共部分)。
Python 集合并交差运算示例运行
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}

print(a | b)                    # 并集
print(a.union(b))
print(a & b)                    # 交集
print(a.intersection(b))
print(a - b)                    # 差集:在 a 不在 b
print(a.difference(b))
print(a ^ b)                    # 对称差
print(a.symmetric_difference(b))

这些运算默认返回新集合,一般不会改写原来的 ab。若需要原地更新,可以使用带 updateintersection_update 等字样的方法。

集合关系判断

除了算出一个新集合,还可以直接问是不是子集或超集、有没有交集:

a = {1, 2, 3}
b = {1, 2}

print(b.issubset(a))     # True
print(a.issuperset(b))   # True
print(a.isdisjoint({7})) # True,没有交集

常见误区

  • 空集合必须写 set(){} 表示空字典。
  • {[1, 2]} 会因列表不可哈希而触发 TypeError
  • 遍历次序不固定,业务判断里不要拿第几个元素当稳定约定。