python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python字典查得快

Python字典为什么查得快详解(3个实验看懂哈希表底层)

作者:青春不朽512

字典(dict)是Python中常见的数据结构,用于存储键值对,这篇文章主要介绍了Python字典为什么查得快的相关资料,文中通过3个实验看懂哈希表底层,需要的朋友可以参考下

系列:Python 底层设计逻辑|类型:原理拆解型|分发:CSDN / 博客园 / 腾讯云
字数:约 1600 字

结论先行:dict 查得快,不是因为它"会找",而是因为它"会算"——用键的哈希值直接算出该去哪个槽位拿数据,理论上一步到位,和字典里存了多少个键几乎无关。但这份快是有代价的:键必须可哈希、查找实际是 O(1) 平均而非最坏、还白放着约三分之一的内存。这三个坑,用三个实验看清楚。

1. 先建立直觉:dict 不是"表",是"带传送门的格子间"

列表查一个值,得从第 0 个开始比,最坏翻到底——O(n)。字典不干这事:

d = {'name': '张三', 'age': 18}
d['age']          # 不是遍历,是 hash('age') → 算出槽位 → 直接取

每个键进来,先算 hash(key),再按槽位数量取模,落进对应的格子。查找同理:算一次哈希,去对应格子拿。格子数远大于元素数时,一步就中。

2. 实验一:10 万个键,dict 和 list 差多少

import time

N = 100000
lst = list(range(N))
dct = {i: i for i in range(N)}
target = N - 1                     # 查最后一个,list 的最坏情况

t0 = time.perf_counter()
_ = target in lst
t1 = time.perf_counter()
_ = target in dct
t2 = time.perf_counter()

print(f'list: {(t1-t0)*1000:.3f} ms')
print(f'dict: {(t2-t1)*1000:.3f} ms')

典型输出:list 要几百毫秒,dict 只要几微秒——差 2~3 个数量级。把 N 再放大 10 倍,dict 的时间几乎不变,list 线性变长。 这一条就是"哈希表 O(1)"的体感。

3. 实验二:哈希冲突是常态,不是事故

槽位总比键少,冲突必然发生。CPython 用开放寻址(探测下一个空槽)解决:

# 构造一批"爱打架"的键:哈希值对 8 同余
keys = [i for i in range(64) if hash(i) % 8 == 0]
d = {k: k for k in keys}
print(len(d))                      # 8 个键全在,查找依然正确

为什么重要:冲突不丢数据,但会让查找从"一步"变成"探几步"。键太集中(或有人恶意构造碰撞键)时,dict 会退化成慢查找——这也是为什么 PYTHONHASHSEED 默认随机化:让攻击者算不出你的字符串哈希。

4. 实验三:为什么 list 不能做键

d = {}
d[[1, 2]] = 'x'        # TypeError: unhashable type: 'list'
d[(1, 2)] = 'x'        # tuple 可以
print(d)               # {(1, 2): 'x'}

为什么翻车:list 内容能改,改了哈希就变了——今天算出的槽位在 A,明天值一改槽位算到 B,字典就永远找不到它了。所以 Python 要求键可哈希:hash() 能算、且对象生命周期内哈希不变(不可变)。tuple 元素全不可变时才可哈希:([1],) 照样报错。

5. 两个反直觉点

① dict 有序且"记性"在键上:Python 3.7+ dict 保序,但这是实现细节顺带的福利,别依赖它做业务逻辑。

② 空 dict 不省多少,装满才扩容:dict 元素超过容量的 2/3 会触发扩容(重新哈希全部键,一次性 O(n))。所以预分配大字典没有意义——扩容均摊下来仍是 O(1)。

6. dict vs set vs list(选型对照)

维度dictsetlist
查找O(1) 平均O(1) 平均O(n)
键/元素要求可哈希可哈希无要求
存什么键→值去重的键有序序列
内存偏高(约 1/3 空闲槽)偏高紧凑
典型场景映射/缓存/计数去重/成员判断顺序处理

对照结论:按 key 取值 → dict;只问"在不在" → set;要顺序、要索引 → list。别用 list 做"存在性检查",数据一上万就是灾难。

到此这篇关于Python字典为什么查得快的文章就介绍到这了,更多相关Python字典查得快内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文