Python 3.7+ dict 有序源于紧凑哈希表结构:indices 数组存索引偏移,entries 数组按插入顺序存储三元组,遍历 entries 自然保序;3.6 虽实现但未规范,不可依赖;dict.keys() 不支持下标因返回视图对象而非列表。

紧凑哈希表结构让 dict 保序,不是靠排序
Python 3.7+ 的 dict 有序,根本不是运行时维护顺序或插入时做排序,而是底层数据结构变了:CPython 把原来的稀疏哈希表换成了「紧凑哈希表」(compact hash table)。它用两个数组分工协作:indices 数组只存索引偏移(用于 O(1) 查找),entries 数组严格按插入顺序追加 [hash, key, value] 三元组。遍历时直接顺序读 entries,自然就保序——这是结构决定的,不是逻辑补丁。
为什么 3.6 看似有序却不能依赖
Python 3.6 实现了紧凑哈希表,行为上已保序,但官方文档没写进语言规范。这意味着:
- PyPy、Jython 等非 CPython 实现可能不遵循该行为
- 某些极端构造方式(如多线程并发插入、极小容量下反复增删)在 3.6 中偶有顺序抖动
- 你写的代码若跑在 3.6 环境里,CI 测试通过不代表它在所有 3.6 兼容实现中都稳定
3.7 起,这个行为被写入 PEP 508 和语言规范,所有合规 Python 实现必须保证。
dict.keys()[0] 报错不是因为无序,而是类型不对
即使字典有序,dict.keys() 返回的是 dict_keys 视图对象,它没有实现 __getitem__,所以不支持下标访问。这不是 bug,是设计使然:
立即学习“Python免费学习笔记(深入)”;
-
d.keys()[0]→TypeError: 'dict_keys' object is not subscriptable -
list(d.keys())[0]可行,但会强制构建完整列表,O(n) 开销,高频路径慎用 - 真要按位置操作(比如取第一个键、移动某键到末尾),该用
collections.OrderedDict,它提供popitem(last=False)和move_to_end()
JSON 序列化保序的前提是字典构造方式,不是版本本身
json.dumps() 输出顺序完全依赖 dict 的迭代顺序,但这个顺序由你怎么构建字典决定:
-
{'a': 1, 'b': 2}:CPython 3.7+ 解析器按书写顺序插入,没问题 -
{**d1, **d2}:顺序是d1插入顺序 +d2插入顺序,不是语法顺序 -
json.loads(json_str)在 3.7+ 返回有序dict,但中间若调用d.update()或重复赋值同名键,后一次赋值会覆盖位置,顺序可能被重排 -
json.dumps(..., sort_keys=True)会强制按键字母序输出,跟插入顺序无关
最易忽略的一点:即使你用的是 Python 3.9,如果字典是从旧版 JSON 反序列化再手动拼接出来的,而没注意更新逻辑,顺序也可能悄悄丢失。


















