Python 里除了列表、元组、字典,还有一个经常被新手忽略的内置类型:集合(set)。它最大的特点是元素不重复、无序,天生适合做去重和数学集合运算。今天这篇就带大家把 set 的常用玩法过一遍。

创建集合:花括号与 set() 函数

创建集合有两种方式:直接用花括号包住元素,或者用 set() 构造函数。注意空的花括号 {} 创建的是字典,空集合必须写 set()。

# 用花括号创建
fruits = {"apple", "banana", "orange"}
print(type(fruits))        # <class 'set'>

# 空集合必须用 set()
empty = set()
print(type(empty))         # <class 'set'>
print(type({}))            # <class 'dict'>,花括号默认是字典

# 用 set() 把字符串、列表转成集合
chars = set("hello")
print(chars)               # {'h', 'e', 'l', 'o'}

集合是无序的,不能用 fruits[0] 按位置取元素,也没有切片操作。

自动去重:一行代码去掉重复元素

往集合里加已存在的元素不会有任何效果,这个特性让 set 成为最顺手的去重工具。

nums = [1, 3, 2, 3, 1, 5, 2]
unique = set(nums)
print(unique)              # {1, 2, 3, 5}

# 去重后如果还想要列表,包一层 list() 再排序
sorted_unique = sorted(set(nums))
print(sorted_unique)       # [1, 2, 3, 5]

给列表去重再排序,两行就够了。

增删元素:add、remove 与 discard

skills = {"python", "sql"}

skills.add("git")          # 添加一个元素
print(skills)              # {'python', 'sql', 'git'}

skills.add("git")          # 再加一次没变化
print(len(skills))         # 3

skills.remove("sql")       # 删除指定元素,不存在会报 KeyError
# skills.remove("java")    # KeyError: 'java'

skills.discard("java")     # discard 删除不存在的元素不报错
print(skills)              # {'python', 'git'}

记住一个小区别:remove() 删不存在的元素会抛异常,discard() 不会。不确定元素在不在时用 discard() 更省心。

交并差:&、|、- 三种运算

set 最有魅力的地方是支持数学里的集合运算,两个集合一组合并能直接算出交集、并集、差集。

frontend = {"html", "css", "js"}
backend = {"python", "js", "sql"}

# 交集 &:两边都有的
print(frontend & backend)  # {'js'}

# 并集 |:合在一起去重
print(frontend | backend)  # {'html', 'css', 'js', 'python', 'sql'}

# 差集 -:只在左边有的
print(frontend - backend)  # {'html', 'css'}
print(backend - frontend)  # {'python', 'sql'}

也可以用方法形式:frontend.intersection(backend) 等价于 &,union() 等价于 |,difference() 等价于 -。运算符写法更简洁,方法名更直白。

实际场景比如:有两份用户名单,list_a & list_b 直接得到两边都出现的人;list_a - list_b 得到只在第一份名单里的人。

子集与成员判断

py = {"python"}
langs = {"python", "java", "go"}

print(py <= langs)         # True,py 的元素都在 langs 里
print(langs <= py)         # False

print("python" in langs)   # True
print("rust" in langs)     # False

顺带说一个性能点:in 在列表上是逐个比较,越长越慢;在集合上是哈希查找,几乎不耗时。频繁判断存在性的场景,把列表换成集合是零成本优化。

小结

  • 集合用花括号创建(空集合是 set()),元素不重复、无序,不支持索引和切片
  • set(列表) 一行去重,配合 sorted() 得到有序不重复列表
  • 增删用 add()、remove()(不存在报错)和 discard()(不存在不报错)
  • & 交集、| 并集、- 差集,<= 判断子集,对应方法名是 intersection、union、difference、issubset
  • 成员判断 in 在集合上远快于列表,频繁查存在性时优先用 set