Python 集合类型深度解析:set 与 frozenset 的核心特性与应用
核心概念:可变与不可变集合
在 Python 中,集合(Set)是一种基于哈希表实现的无序且不重复的数据结构。它主要分为两种类型:set 和 frozenset。
- set(可变集合):支持动态添加和移除元素。由于其内容可变,因此它是不可哈希的(unhashable),不能作为字典的键或另一个集合的元素。主要用于去重和关系运算。
- frozenset(不可变集合):创建后其元素无法更改。这种不可变性使其具备哈希值,因此可以作为字典的键或嵌套在其他集合中。代价是失去了
add()和remove()等修改方法。
一、 集合的初始化与构建
可以通过内置的工厂函数 set() 和 frozenset() 来创建集合,传入的参数必须是可迭代对象。此外,set 还可以使用花括号 {} 字面量直接创建(注意空集合必须用 set(),因为 {} 会创建空字典)。
# 使用工厂函数创建可变集合
dynamic_set = set(['apple', 'banana', 'cherry', 'apple'])
print(dynamic_set) # 输出: {'apple', 'cherry', 'banana'} (自动去重)
print(type(dynamic_set)) # 输出: <class 'set'>
# 使用字面量语法创建
literal_set = {'dog', 'cat', 'bird'}
print(literal_set) # 输出: {'dog', 'cat', 'bird'}
# 创建不可变集合
static_set = frozenset(['red', 'green', 'blue', 'red'])
print(static_set) # 输出: frozenset({'red', 'green', 'blue'})
二、 状态变更与元素操作
只有 set 允许在创建后修改其内容。frozenset 尝试调用修改方法会抛出 AttributeError。
numbers = {10, 20, 30}
# 添加单个元素
numbers.add(40)
# 批量更新元素
numbers.update([50, 60, 20])
# 移除元素(若不存在则抛出 KeyError)
numbers.remove(10)
# 安全移除(若不存在则静默忽略)
numbers.discard(99)
# 使用运算符移除
numbers -= {30, 40}
print(numbers) # 输出: {50, 20, 60}
# 尝试修改不可变集合
frozen_nums = frozenset([1, 2, 3])
# frozen_nums.add(4) # 触发 AttributeError: 'frozenset' object has no attribute 'add'
三、 成员检测与关系判定
集合提供了高效的成员资格测试以及丰富的数学关系判定。
set_a = {'x', 'y', 'z'}
set_b = frozenset(['x', 'y', 'w'])
# 成员检测
print('x' in set_a) # True
print('w' not in set_a) # True
# 等价与不等价测试
print(set_a == set_b) # False
print(set({'x', 'y'}) != set_a) # True
# 子集与超集判定
print({'x', 'y'} < set_a) # True (严格子集)
print(set_a >= {'x'}) # True (超集)
print(set_a.issubset({'x', 'y', 'z', 'w'})) # True
四、 遍历集合元素
无论是可变还是不可变集合,都支持迭代协议。由于集合是无序的,遍历输出的顺序可能与插入顺序不同。
colors = frozenset(['cyan', 'magenta', 'yellow'])
for color in colors:
print(color)
# 输出顺序不固定,例如: yellow, cyan, magenta
五、 数学集合运算
Python 为集合提供了直观的运算符和对应的方法,用于执行标准的数学集合操作。
1. 并集 (Union)
包含两个集合中的所有唯一元素。运算符:|,方法:union()。
s1 = {1, 2, 3}
s2 = {3, 4, 5}
print(s1 | s2) # {1, 2, 3, 4, 5}
print(s1.union(s2)) # {1, 2, 3, 4, 5}
2. 交集 (Intersection)
包含同时存在于两个集合中的元素。运算符:&,方法:intersection()。
print(s1 & s2) # {3}
print(s1.intersection(s2)) # {3}
3. 差集 (Difference)
包含在第一个集合中但不在第二个集合中的元素。运算符:-,方法:difference()。
print(s1 - s2) # {1, 2}
print(s1.difference(s2)) # {1, 2}
4. 对称差集 (Symmetric Difference)
包含只属于其中一个集合的元素(排除交集部分)。运算符:^,方法:symmetric_difference()。
print(s1 ^ s2) # {1, 2, 4, 5}
print(s1.symmetric_difference(s2)) # {1, 2, 4, 5}
5. 混合类型运算的返回规则
当 set 和 frozenset 混合进行运算时,返回结果的类型由左操作数决定。
mutable = {1, 2}
immutable = frozenset([2, 3])
print(type(mutable | immutable)) # <class 'set'>
print(type(immutable | mutable)) # <class 'frozenset'>
六、 核心方法与操作符速查表
通用方法与操作符(适用于 set 和 frozenset)
| 操作/方法 | 等价运算符 | 功能描述 |
|---|---|---|
len(s) | - | 返回集合中元素的数量 |
obj in s | - | 判断 obj 是否存在于 s 中 |
obj not in s | - | 判断 obj 是否不存在于 s 中 |
s == t | == | 判断两个集合是否包含完全相同的元素 |
s < t | < | 严格子集测试(s 是 t 的子集且 s != t) |
s.issubset(t) | <= | 子集测试(允许 s == t) |
s > t | > | 严格超集测试 |
s.issuperset(t) | >= | 超集测试(允许 s == t) |
s.union(t, ...) | | | 返回多个集合的并集 |
s.intersection(t, ...) | & | 返回多个集合的交集 |
s.difference(t, ...) | - | 返回差集 |
s.symmetric_difference(t) | ^ | 返回对称差集 |
s.copy() | - | 返回集合的浅拷贝 |
可变集合专属方法(仅适用于 set)
| 方法 | 等价运算符 | 功能描述 |
|---|---|---|
s.update(t, ...) | |= | 将其他集合或可迭代对象中的元素合并到 s 中 |
s.intersection_update(t, ...) | &= | 仅保留 s 与其他集合共有的元素 |
s.difference_update(t, ...) | -= | 从 s 中移除也存在于其他集合中的元素 |
s.symmetric_difference_update(t) | ^= | 更新 s 为对称差集 |
s.add(elem) | - | 向集合中添加单个元素 |
s.remove(elem) | - | 移除指定元素,若不存在则抛出 KeyError |
s.discard(elem) | - | 移除指定元素,若不存在则不执行任何操作 |
s.pop() | - | 随机移除并返回一个元素,若集合为空则抛出 KeyError |
s.clear() | - | 清空集合中的所有元素 |
