在 Python 中,dict 几乎无处不在。
我们用它保存配置:
config = {
"host": "localhost",
"port": 8080,
}
用它表示 JSON 对象:
user = {
"name": "Alice",
"age": 18,
}
甚至 Python 自身也大量依赖字典:
1)globals() # 模块全局变量
2)obj.__dict__ # 对象属性
3)Class.__dict__ # 类属性
因此,dict 的性能不只是影响字典操作,也会影响变量查找、对象属性访问、函数关键字参数等大量 Python 代码。
dict底层设计是哈希表,这是每门编程语言都绕不开的设计,哈希表的设计整体量大实现分为两类:拉链法和开放寻址方法。
Python 实现选择的是后者:开放寻址。
包括 Golang,Rust都是选择的开放寻址这个方向,因为相对于拉链法,开放寻址是整体顺序存储,CPU读写更快,Cache命中率更高(现代 CPU 更喜欢连续内存)。
说回Python的实现,如今的 CPython dict 实现同时具备几个看起来相互矛盾的特征:
1)查询平均接近 O(1);
2)插入和删除通常很快;
3)内存占用相对紧凑;
4)遍历速度快;
5)保持插入顺序。
这些能力都是在几十年的演化中,一次次解决旧设计暴露的问题。
这条演化路线可以简单概括为:
开放寻址(如图2
↓
更合理的冲突探测
↓
墓碑解决删除问题(如图3
↓
控制负载因子和扩容
↓
针对小字典和字符串 key 优化(如图4
↓
实例字典共享 key(如图5
↓
稀疏索引 + 紧密数据
↓
Compact Dict(如图6