目录
[一、一句话定义 frozenset](#一、一句话定义 frozenset)
[二、为什么普通 set 不够用?](#二、为什么普通 set 不够用?)
[三、frozenset 就是为了解决这个问题而生的](#三、frozenset 就是为了解决这个问题而生的)
[四、frozenset 的基本用法](#四、frozenset 的基本用法)
[1️⃣ 创建 frozenset](#1️⃣ 创建 frozenset)
[2️⃣ 和 set 一样支持集合运算](#2️⃣ 和 set 一样支持集合运算)
[3️⃣ 不能修改(这是重点)](#3️⃣ 不能修改(这是重点))
[五、frozenset 的"真正价值"(核心)](#五、frozenset 的“真正价值”(核心))
[1️⃣ 作为 dict 的 key(非常重要)](#1️⃣ 作为 dict 的 key(非常重要))
[2️⃣ 作为 set 的元素(集合的集合)](#2️⃣ 作为 set 的元素(集合的集合))
[3️⃣ 表达"组合关系"(而不是顺序)](#3️⃣ 表达“组合关系”(而不是顺序))
[六、frozenset vs tuple(很多人会搞混)](#六、frozenset vs tuple(很多人会搞混))
[七、frozenset 在真实项目中的典型场景](#七、frozenset 在真实项目中的典型场景)
[场景 1:权限 / 角色系统](#场景 1:权限 / 角色系统)
[场景 2:规则匹配引擎](#场景 2:规则匹配引擎)
[场景 3:缓存 Key(高级用法)](#场景 3:缓存 Key(高级用法))
[九、frozenset 的设计哲学(很 Python)](#九、frozenset 的设计哲学(很 Python))
[十、什么时候你"应该考虑 frozenset"?](#十、什么时候你“应该考虑 frozenset”?)
在 Python 中,大多数人都熟悉:
-
list -
tuple -
set -
dict
但很少有人认真用过:
frozenset
它看起来像个"冷门类型",但其实是 Python 类型体系中非常重要的一块拼图。
一、一句话定义 frozenset
frozenset是一个"不可变的 set"
也就是说:
-
✅ 元素唯一(和 set 一样)
-
✅ 无序
-
❌ 不能修改
-
✅ 可以作为 dict 的 key
-
✅ 可以放进 set
二、为什么普通 set 不够用?
先看一个常见报错 👇
s = set([1, 2, 3]) d = {s: "hello"}
结果:
TypeError: unhashable type: 'set'
原因是:
set 是可变的 → 不可 hash → 不能作为 key
Python 有一个硬规则:
只有"不可变对象"才能当字典 key
三、frozenset 就是为了解决这个问题而生的
fs = frozenset([1, 2, 3]) d = { fs: "hello" }
✔️ 完全合法
✔️ 安全
✔️ 可预测
四、frozenset 的基本用法
1️⃣ 创建 frozenset
fs1 = frozenset([1, 2, 3]) fs2 = frozenset("abc")
2️⃣ 和 set 一样支持集合运算
a = frozenset([1, 2, 3]) b = frozenset([3, 4, 5]) a | b # 并集 a & b # 交集 a - b # 差集 a ^ b # 对称差集
👉 运算结果仍然是 frozenset
3️⃣ 不能修改(这是重点)
fs.add(4)
❌ 报错:
AttributeError: 'frozenset' object has no attribute 'add'
👉 这不是缺点,是 设计目的
五、frozenset 的"真正价值"(核心)
1️⃣ 作为 dict 的 key(非常重要)
permissions = { frozenset(["read", "write"]): "editor", frozenset(["read"]): "viewer" }
这在下面场景极其常见:
-
权限系统
-
规则匹配
-
策略引擎
-
配置系统
2️⃣ 作为 set 的元素(集合的集合)
groups = { frozenset([1, 2]), frozenset([2, 3]), frozenset([3, 4]) }
👉 用 frozenset 才能构建"多层集合结构"
3️⃣ 表达"组合关系"(而不是顺序)
team = frozenset(["张三", "李四", "王五"])
含义是:
-
成员不重复
-
顺序不重要
-
组合是固定的
👉 语义非常清晰
六、frozenset vs tuple(很多人会搞混)
| 对比点 | frozenset | tuple |
|---|---|---|
| 是否有序 | ❌ | ✅ |
| 是否可变 | ❌ | ❌ |
| 去重 | ✅ | ❌ |
| 集合运算 | ✅ | ❌ |
| 适合表达 | 关系 / 组合 | 顺序 / 位置 |
选择原则一句话:
顺序重要 → tuple
成员重要 → frozenset
七、frozenset 在真实项目中的典型场景
场景 1:权限 / 角色系统
ROLE_MAP = { frozenset(["read"]): "guest", frozenset(["read", "write"]): "user", frozenset(["read", "write", "delete"]): "admin" }
场景 2:规则匹配引擎
rules = { frozenset(["A", "B"]): "rule_1", frozenset(["B", "C"]): "rule_2" }
场景 3:缓存 Key(高级用法)
cache_key = frozenset(params.items())
👉 避免顺序导致的 cache miss
八、为什么你平时"几乎没见过它"?
因为:
-
Web CRUD 用不到
-
普通业务逻辑用不到
-
新手教程刻意回避
👉 但在"框架 / 中间件 / 底层库"中,它非常常见
九、frozenset 的设计哲学(很 Python)
"如果一个东西逻辑上不应该被改,那就让它改不了。"
这是 Python 的一贯风格:
| 类型 | 设计意图 |
|---|---|
| tuple | 不可变序列 |
| str | 不可变文本 |
| frozenset | 不可变集合 |
十、什么时候你"应该考虑 frozenset"?
你可以问自己 3 个问题:
1️⃣ 数据是否需要去重?
2️⃣ 数据是否不关心顺序?
3️⃣ 数据是否逻辑上不该被修改?
✔️ 全是 → frozenset
十一、一句话总结
frozenset 不是"冷门类型",而是"语义型类型"
用它不是为了炫技,而是为了:
👉 表达清楚"这是一组不可改变的关系"
十二、下一步你可以继续深入的方向
如果你愿意,我可以继续给你写:
-
✅ 为什么 dict 的 key 一定要 hashable?
-
✅ Python 中 hash 到底是什么?
-
✅ set / frozenset 的底层实现原理(通俗版)
-
✅ frozenset 在缓存系统中的高级玩法