dict 和 set 在 CPython 中快是因为底层用高度优化的哈希表,平均时间复杂度 O(1),但性能受哈希函数、冲突、内存布局及构造方式等影响;dict.fromkeys() 批量初始化更快;成员检测优先用 set;__slots__ 可节省内存并加速属性访问。

为什么 dict 和 set 在 CPython 中快得不像 Python?
因为底层用的是哈希表(hash table),且 CPython 实现高度优化:插入、查找、删除平均时间复杂度都是 O(1)。但这个“快”有前提——哈希函数要快,冲突要少,内存布局要友好。
常见误区是以为“只要用 dict 就一定快”,其实构造方式、键类型、扩容时机都会显著影响性能:
-
dict()空初始化比{}略慢(字节码多一条CALL_FUNCTION) - 用可变对象(如
list)当dict键会直接报TypeError: unhashable type - 一次性插入大量数据时,预设容量能避免多次 rehash——但 Python 不支持直接指定初始大小,只能靠“先建再更新”的 trick
如何让 dict.fromkeys() 成为批量初始化的首选?
当你需要一个键存在、值统一的字典(比如去重后标记为 True),dict.fromkeys(iterable, value) 比循环 dict[key] = value 快 2–3 倍,因为它绕过了 Python 层的键值对赋值逻辑,直接在 C 层批量构建哈希表。
注意两个坑:
立即学习“Python免费学习笔记(深入)”;
- 第二个参数是引用传递:如果传的是可变对象(如
[]),所有键共享同一个实例 → 改一个全改 - 它不检查键是否重复:输入含重复元素时,结果字典只保留最后一个出现的键
示例:
valid_keys = ["a", "b", "c"]
flags = dict.fromkeys(valid_keys, False) # ✅ 安全
cache = dict.fromkeys(valid_keys, {}) # ❌ 危险:所有键共用一个 dict
什么时候该用 set 而不是 list 做成员检测?
只要涉及 if x in container:,且容器长度 > 100,几乎总是该换 set。列表是 O(n) 线性扫描,集合是 O(1) 哈希查找——10 万元素下,前者可能耗时 10ms+,后者稳定在 0.1ms 内。
但别忽略代价:
- 构建
set本身要时间:从list转set是 O(n),如果只查一次,不如直接遍历 -
set占内存更大:每个元素额外存哈希值 + 更宽松的负载因子(默认 0.625),实际内存占用约是等效list的 2–3 倍 - 顺序不保留(Python 3.7+
dict有序,但set依然无序)
__slots__ 对自定义类里用 dict 存属性的影响
如果你写了一个高频实例化的类,又习惯用 self.__dict__ 动态存属性(比如解析 JSON 后挂载字段),那它的 dict 其实是每个实例独占的一份哈希表。此时开启 __slots__ 能省下每实例 ~50 字节,并加快属性访问——因为绕过了 __dict__ 查找路径。
但要注意限制:
- 一旦定义
__slots__,实例就不再有__dict__,不能动态新增未声明的属性 - 继承链中只要有一个父类用了
__slots__,子类也必须显式定义(哪怕为空__slots__ = ()),否则报错 - 如果类本身需要
__dict__(比如用vars()或某些 ORM),就不能用__slots__
典型适用场景:DTO 类、解析后的消息体、高频创建的中间对象。










