python 3.7+ 字典更省内存是因采用紧凑字典结构,平均节省20%~25%:旧版用稀疏哈希表致近1/3内存为占位符,新版拆分为小而稀疏的indices数组和大而紧凑的entries数组,消除指针、填充及哈希缓存三类冗余。

Python 3.7+ 的字典确实更省内存,这不是错觉,而是底层结构从“稀疏哈希表”切换为“紧凑字典”带来的真实收益——平均节省 20%~25% 内存。关键不在“删了什么”,而在“怎么存的”。
为什么老字典(3.5 及之前)内存浪费严重?
旧版 dict 用一个大而稀疏的 PyDictEntry 数组,初始大小通常是 8、16、32……但实际只填满约 2/3 就会扩容。数组里大量位置是 NULL(空槽),遍历时要跳过它们,顺序完全不可预测。
- 每个空槽仍占完整结构体空间(
me_hash+me_key+me_value指针) - 负载因子低(常 ≤ 0.66),意味着近 1/3 内存纯属“占位符”
- 哈希碰撞时靠线性探测或伪随机探测找下一个空位,进一步加剧碎片
紧凑字典(3.6+ CPython,3.7+ 标准)怎么省的?
它把存储拆成两个物理分离的数组:indices(小而稀疏)和 entries(大而紧凑)。省空间的核心在于“解耦索引与数据”。
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
-
indices是int8_t或int16_t数组,只存偏移量或标记(如-1表示空),每个元素仅 1~2 字节 -
entries是连续追加的键值对列表,无空洞,按插入顺序排列,hash、key、value紧挨着存 - 查找时:先算哈希 → 映射到
indices下标 → 读出该位置的整数 → 作为下标去entries里取真实数据
省下来的内存都去哪儿了?
不是凭空消失,而是结构重排后消除了三类冗余:
-
指针冗余:旧结构每个槽都要存三个指针;新结构中
entries是连续块,indices只存短整型索引 -
填充冗余:旧结构为防哈希冲突预留大量空槽;新结构靠
indices的稀疏性承担“寻址跳转”,entries可以塞得更满 -
哈希缓存冗余:旧结构每个槽重复存
me_hash;新结构中哈希值只在entries存一份,且用于冲突判定时复用率高
注意:有序 ≠ 排序,省空间 ≠ 无代价
紧凑字典的“有序”是插入顺序的副产品,不是靠额外排序实现的——这点常被误解。但它对某些场景有隐性影响:
- 小字典(
len(d) )可能反而略慢,因多一次 <code>indices查表跳转 -
popitem(last=False)(弹出第一个)在 3.7+ 是 O(n),因为要从entries开头找非空项;而last=True(默认)仍是 O(1) - 内存节省在大量小字典场景(如对象属性、JSON 解析结果)中效果最明显,单个超大字典收益比例会收敛
真正容易被忽略的是:这个优化只存在于 CPython;PyPy 在 7.3.11+ 才逐步跟进紧凑结构,Jython 和 MicroPython 仍用传统实现——跨实现移植时别把内存表现当常量用。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










