在 Python 3 中,集合(Set) 是一种极其强大且高效的数据结构。它的核心特征是:无序、元素唯一、基于哈希表实现。
如果说列表(List)是用来“存储顺序”的,字典(Dict)是用来“建立映射”的,那么集合(Set)就是用来 “去重”和“快速查找” 的。
下面我将从基础操作、核心数学运算、高频应用场景到常见陷阱,为你全面解析 Python 3 的集合。
一、 创建集合
有两种创建集合的方式,但有一个极其重要的陷阱需要注意。
# 1. 使用花括号 {} (最常用)
fruits = {"apple", "banana", "cherry"}
print(type(fruits)) # <class 'set'>
# 2. 使用 set() 构造函数 (常用于将其他可迭代对象转换为集合)
numbers = set([1, 2, 2, 3, 3, 3])
print(numbers) # 输出: {1, 2, 3} (自动去重)
# ⚠️ 致命陷阱:如何创建空集合?
empty_dict = {} # ❌ 这是空字典 (dict)
empty_set = set() # ✅ 这才是空集合 (set)二、 基本增删改查
由于集合是无序的,它不支持索引(如 my_set[0] 会报错),也不支持切片。
my_set = {"apple", "banana"}
# 1. 添加元素
my_set.add("cherry") # 添加单个元素
my_set.update(["orange", "grape"]) # 批量添加多个元素 (可传入任何可迭代对象)
# 2. 删除元素
my_set.remove("banana") # 删除指定元素,如果不存在会抛出 KeyError
my_set.discard("watermelon") # 删除指定元素,如果不存在**不会报错** (推荐)
popped_item = my_set.pop() # 随机删除并返回一个元素 (因为集合无序)
# 3. 清空集合
my_set.clear()三、 核心优势:数学集合运算 🌟
这是 Set 最迷人的地方。Python 允许你使用直观的运算符(或对应的方法)来执行标准的数学集合运算,且性能极高。
set_a = {1, 2, 3, 4, 5}
set_b = {4, 5, 6, 7, 8}
# 1. 交集 (Intersection):两者都有的元素
print(set_a & set_b) # 运算符: {4, 5}
print(set_a.intersection(set_b)) # 方法
# 2. 并集 (Union):两者的所有元素(自动去重)
print(set_a | set_b) # 运算符: {1, 2, 3, 4, 5, 6, 7, 8}
print(set_a.union(set_b))
# 3. 差集 (Difference):在 A 中但不在 B 中的元素
print(set_a - set_b) # 运算符: {1, 2, 3}
print(set_a.difference(set_b))
# 4. 对称差集 (Symmetric Difference):只在 A 或只在 B 中的元素 (非交集部分)
print(set_a ^ set_b) # 运算符: {1, 2, 3, 6, 7, 8}
print(set_a.symmetric_difference(set_b))💡 提示:运算符(&, |, -, ^)只能用于两个集合之间;而方法(.intersection() 等)可以接收任何可迭代对象(如列表),更加灵活。
四、 高频实战应用场景
场景 1:极速去重 (Deduplication)
这是 Set 最常见的用途。将列表转换为集合再转回列表,即可去重。
raw_data = [1, 2, 2, 3, 4, 4, 5]
unique_data = list(set(raw_data))
print(unique_data) # 输出: [1, 2, 3, 4, 5] (注意:顺序可能会改变)⚠️ 进阶技巧:如果去重后必须保持原有顺序,不要用 Set,请用字典(Python 3.7+ 字典保持插入顺序):
unique_ordered = list(dict.fromkeys(raw_data))场景 2:O(1) 极速成员检查 (Membership Testing)
当需要频繁检查某个元素是否存在于大量数据中时,绝对不要用 List,一定要用 Set。
# 假设有 100 万个用户 ID
valid_ids_list = [1001, 1002, ..., 999999] # 查找时间复杂度: O(n)
valid_ids_set = set(valid_ids_list) # 查找时间复杂度: O(1)
user_id = 500000
# ❌ 慢:需要遍历列表
if user_id in valid_ids_list:
pass
# ✅ 快:基于哈希表直接定位,瞬间完成
if user_id in valid_ids_set:
pass场景 3:集合推导式 (Set Comprehension)
与列表推导式语法类似,但使用花括号,生成的是集合。
# 获取 0-9 中所有偶数的平方,并自动去重
squares = {x**2 for x in range(10) if x % 2 == 0}
print(squares) # 输出: {0, 16, 64, 36, 4} (无序)五、 不可变集合:frozenset
普通的 set 是可变的(Mutable),因此它不能作为字典的键 (Key),也不能放在另一个集合中。
如果你需要一个不可变的集合,可以使用 frozenset。
# 创建冻结集合
frozen = frozenset([1, 2, 3])
# frozen.add(4) # ❌ AttributeError: 'frozenset' object has no attribute 'add'
# ✅ 应用场景 1:作为字典的键
permissions = {
frozenset(["read", "write"]): "Editor",
frozenset(["read"]): "Viewer"
}
print(permissions[frozenset(["read"])]) # 输出: Viewer
# ✅ 应用场景 2:作为集合的元素 (集合的集合)
set_of_sets = {frozenset([1, 2]), frozenset([3, 4])}六、 常见陷阱与最佳实践
1. 集合的元素必须是“可哈希的” (Hashable)
因为集合底层是哈希表,所以放入集合的元素必须是不可变的(如整数、字符串、元组)。不能将列表、字典或其他普通集合放入集合中。
# ❌ TypeError: unhashable type: 'list'
my_set = {1, 2, [3, 4]}
# ✅ 正确做法:将内部列表转为元组
my_set = {1, 2, (3, 4)}2. 性能对比总结 (何时使用什么?)
| 需求 | 推荐数据结构 | 原因 |
|---|---|---|
| 需要保持顺序,允许重复 | list | 有序,支持索引 |
| 需要键值对映射 | dict | 快速通过 Key 查找 Value |
| 需要快速判断“是否存在” (in) | set | 哈希查找,O(1) 时间复杂度 |
| 需要去重或进行交/并/差集运算 | set | 内置数学运算,代码极简且高效 |
总结
Python 的集合(Set)不仅仅是一个“去重工具”,它是处理唯一性和关系运算的利器。记住两个黄金法则:
- 需要极速查找 (
in) 时,把列表转成集合。 - 需要比较两组数据的重叠或差异时,毫不犹豫地使用
&,|,-,^。
你目前是否有处理大量数据去重,或者需要比对两个列表差异的具体需求?我可以帮你用 Set 写出最优雅的代码!