大家好,我是正在实战各种 AI 项目的程序员晚枫。
列表的动态数组、字典的哈希表、集合的实现原理。这一讲,让你彻底理解 Python 容器类型的性能秘密。
📖 开篇:为什么列表可以用索引访问?
1 | lst = [10, 20, 30, 40] |
C 语言的数组可以用索引 O(1) 访问,Python 的列表也是——因为它底层就是动态数组!
📋 PyListObject(列表)
1 | // Include/listobject.h |
动态数组原理
1 | allocated = 8 (已分配空间) |
扩容策略
1 | // 当元素数量达到 allocated 时,触发扩容 |
性能真相
1 | import time |
🗂️ PyDictObject(字典)
Python 3.6+ 的字典发生了革命性变化——从「哈希表 + 开放寻址」变成「紧凑数组」:
旧版实现(Python 2.7 / 3.5 之前)
1 | 哈希表:[slot0, slot1, slot2, slot3, ...] |
新版实现(Python 3.6+,DictProtocol)
1 | d = {} # 空字典 |
新结构:
keys数组:存储哈希值 + 键索引(类似稀疏数组)values数组:按插入顺序存储值(紧凑数组)key数组:按插入顺序存储键(紧凑数组)
1 | // Objects/dictobject.c |
哈希冲突解决
1 | # 字典查找过程: |
🔮 set(集合)
set 底层和字典的 keys 数组几乎一样,只是没有 values:
1 | s = {1, 2, 3, 4, 5} |
set 的性能特点
| 操作 | 平均复杂度 | 说明 |
|---|---|---|
| 添加/删除 | O(1) | 哈希查找 |
| 成员检查 | O(1) | x in s |
| 交集/并集 | O(min(len(s1), len(s2))) | 遍历较小的集合 |
| 遍历 | O(n) | 按哈希顺序 |
1 | # 集合的高效操作 |
💡 本节作业
- 用
timeit比较列表append和insert(0, x)的性能差异 - 验证字典的插入顺序:
d = {}; d['b']=1; d['a']=2; d['c']=3; print(list(d.keys())) - 写一个程序:用
set对两个列表去重并找交集
🎯 本讲总结
PyListObject:动态数组,over-allocate 扩容策略,append 均摊 O(1)。
PyDictObject:3.6+ 使用紧凑数组,keys + values 分离,保持插入顺序。
PySetObject:类似字典的 keys 数组,无 values,必须可哈希。
性能关键:在列表尾部操作,避免头部插入。
📚 推荐教材
《Python 编程从入门到实践(第 3 版)》 | 《流畅的 Python(第 2 版)》 | 《CPython 设计与实现》
🔗 课程导航
← 上一讲:字符串类型实现 | 下一讲:函数与类实现 →
💬 联系我
| 平台 | 账号/链接 |
|---|---|
| 微信 | 扫码加好友 |
| B 站 | Python 自动化办公社区 |
主营业务:AI 编程培训、企业内训、技术咨询