集合(set)是 Python 内置的数据结构之一,和数学里的集合概念很像:无序、不重复的元素汇集。它的底层实现基于哈希表,因此具有高效的成员检测和去重能力。掌握集合,会让很多数据处理任务变得格外简洁。
去重原理
集合最核心的特性就是元素唯一性。当你把一个序列传给 set() 时,重复的元素会被自动去除。这个“去重”不是靠循环比较,而是依赖哈希表。
- 哈希值决定身份:集合在存储元素前,会先调用
hash()函数计算该元素的哈希值。如果哈希值相同且==比较也相等(考虑到哈希冲突),就认为是同一个元素,不会重复添加。 - 对元素的要求:正因为依赖哈希,集合的元素必须是不可变(hashable)对象。数字、字符串、元组可以放入集合;列表、字典、集合本身则不行,因为它们可变,无法可靠计算哈希值。
- 无序性:哈希表不保证插入顺序,所以集合没有索引,也不能切片。如果你需要保持顺序且去重,可以用
dict.fromkeys(seq).keys()(Python 3.7+ 字典保序)或直接保留原序的手动循环。
实际用法很简单:
nums = [3, 1, 2, 1, 3, 4]
unique_nums = set(nums) # {1, 2, 3, 4}
这个操作的时间复杂度接近 O(n),远比手动双层循环高效。
集合运算
集合支持数学中的典型集合操作,既可以通过方法,也可以通过运算符完成,代码可读性很高。
| 数学概念 | 运算符 | 方法 | 说明 |
|---------|--------|------|------|
| 并集 | \| | union() | 返回两个集合中所有元素(去重) |
| 交集 | & | intersection() | 返回两个集合中共同存在的元素 |
| 差集 | - | difference() | 返回属于第一个集合、但不属于第二个的元素 |
| 对称差集 | ^ | symmetric_difference() | 返回只出现在其中一个集合中的元素(不同时在两个集合中) |
示例:
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
print(a | b) # {1, 2, 3, 4, 5, 6}
print(a & b) # {3, 4}
print(a - b) # {1, 2}
print(a ^ b) # {1, 2, 5, 6}
这些运算符也可以和赋值结合,实现原地更新(如 a |= b 相当于 a.update(b))。
除上述基础运算,集合还提供:
add(elem):添加单个元素。remove(elem)/discard(elem):删除元素,前者在元素不存在时抛出 KeyError,后者不报错。pop():随机弹出某个元素(因为无序,所以无法预测)。issubset()/issuperset():判断子集/超集关系。isdisjoint():判断两个集合是否没有交集。
适用场景
集合在“去重”和“关系比较”方面的优势,使它天然适合以下场景:
- 快速去重
将列表转为集合再转回列表,是最常见的去重写法:
unique_list = list(set(original_list))
注意:这样会丢失原始顺序,如果顺序重要,可用 sorted(set(...), key=original_list.index) 或利用字典键保序。
- 成员检查
判断元素是否存在,集合比列表快得多(O(1) vs O(n))。当数据量大且需要反复in判断时,应优先考虑用集合或字典存储数据。
- 交集/差集比较
- 找出两个列表的共同元素:
set(list1) & set(list2) - 找出一个列表中不在另一个列表的元素:
set(list1) - set(list2) - 这种写法比嵌套循环清晰高效得多,常用于权限校验、标签筛选、数据对账等业务。
- 去除重复字符串或单词
分词后想统计独特词汇:unique_words = set(text.split())。
- 缓存访问记录(如已处理 ID)
爬虫已经爬过的 URL、已发送通知的用户 ID 等,可以用集合保存,每次判断 if id in processed_ids 就能快速跳过。
- 数学集合模型
需要实现数学概念集合操作时,直接用 Python 集合映射,代码和公式非常接近。
补充:不可变集合 frozenset
frozenset 是集合的不可变版本。一旦创建,不能增删元素。它主要用于:
- 作为另一个集合的元素(因为 frozenset 可哈希)。
- 作为字典的键。
- 在需要保证集合内容不会被意外修改的场景。
fs = frozenset([1, 2, 3])
d = {fs: "value"} # 合法
总体而言,集合是 Python 数据处理工具链中的一把“轻巧快刀”,善用它可以大幅减少循环和条件判断,让代码更加简洁、直观、高效。