page contents

Python字典(dict)为什么是无序的(3.6前),3.7后为什么保证插入顺序?

在 Python 面试里,有一个特别高频的“灵魂拷问”——字典 dict 为什么在 3.6 之前是无序的,而从 3.7 开始却变成了“有序”,还能保持插入顺序?很多人只记住了结论,却很少有人能把背后的原理讲清楚。今天咱们就来把这事儿好好掰扯掰扯,顺便聊聊字典这个数据结构在 Python 里的进化史。

attachments-2025-09-5vW4LVgb68be2fafab907.png在 Python 面试里,有一个特别高频的“灵魂拷问”——字典 dict 为什么在 3.6 之前是无序的,而从 3.7 开始却变成了“有序”,还能保持插入顺序?很多人只记住了结论,却很少有人能把背后的原理讲清楚。今天咱们就来把这事儿好好掰扯掰扯,顺便聊聊字典这个数据结构在 Python 里的进化史。

首先要搞明白,所谓“无序”,不是说字典的实现里没有顺序,而是说在语言层面,Python 官方没保证遍历顺序。你在 3.5、3.4 里写个字典,迭代时的 key 顺序可能看着像是乱的,但其实它背后是按照哈希表的存储方式来的。只是哈希表这种玩意儿,随着哈希冲突、扩容、rehash 等操作,key 的位置变化太频繁,所以结果看上去不稳定。Python 当年为了实现性能,不想背负“要保证顺序”的包袱,于是干脆说:字典是无序的,你别对顺序有期待。

那为啥 3.6 又突然“有序”了?这里有点八卦。当时 Python 核心开发团队重写了 dict 的底层实现,用了一种叫“紧凑字典”(compact dict)的方式。这事儿一开始是优化内存和性能,不是为了“有序”。结果阴差阳错,发现这个新实现里,字典遍历的时候刚好就能保持插入顺序。CPython 3.6 就默认启用了这个实现,但注意:那时候官方还说这只是实现细节,不要依赖。到了 3.7,社区一看,大家都习惯了,干脆把“保持插入顺序”写进了语言规范,从此才成了标准。

说到这里,你可能会好奇:compact dict 究竟改了啥?传统哈希表存 key 和 value 一般是两个数组,扩容时还得搬来搬去,碎片一堆。Python 3.6 之前的 dict 就是这么干的,key 的存储和查找位置基本上完全由哈希值决定,没法管顺序。而 3.6 的新实现分成了两块:一块是紧凑的数组,用来按插入顺序存放 key 和 value;另一块是哈希表,负责加速查找。这样一来,遍历时直接扫紧凑数组,就能保持插入顺序;查找时还是用哈希表,速度依旧快。这波操作既优化了内存,又让顺序“白捡”了回来。

我给你举个简单的例子,你就能体会差别:

# Python 3.5d = {'a'1'b'2'c'3}
print(d)  # 可能输出 {'a': 1, 'c': 3, 'b': 2},顺序不一定

# Python 3.7+
d = {'a'1'b'2'c'3}
print(d)  # 一定是 {'a': 1, 'b': 2, 'c': 3}

看上去只是“顺序”不同,但这背后反映的却是底层实现的质变。很多人第一次知道这个改动,都以为是 Python “贴心”地给我们加了新特性,其实这就是性能优化的副作用。

那问题又来了,为什么 Python 要等到 3.7 才写进规范?因为语言规范是承诺,一旦承诺了,未来就不能随便改,否则会导致无数代码崩掉。3.6 里只是实现层面“刚好如此”,不能轻易保证。而等到 3.7,开发者们评估了一圈,觉得这个实现既稳定又好用,于是就官方背书了。

这事儿其实挺能说明 Python 的风格:它不是为了花哨的新特性去炫技,而是优先保证“好用”和“一致性”。顺序遍历本来是免费的副作用,但既然大家都喜欢,就干脆变成标准。相比之下,有些语言(比如 Java 里的 HashMap),直到很晚才强调迭代顺序不可依赖,除非你特地用 LinkedHashMap。Python 等于给你开了个大大的后门,让你少写一堆额外的代码。

不过要提醒一点,虽然 dict 在 3.7 之后保证顺序,但它不是链表,插入中间元素不会影响已有顺序,删除再插入 key 也会让它跑到末尾。这点有时候会踩坑。比如:

d = {'a': 1, 'b': 2, 'c': 3}

del d['b']

d['b'] = 99

print(d)  # 输出 {'a': 1, 'c': 3, 'b': 99}

很多人以为删了再加会回到原来的位置,实际上它是新的插入,跑到最后去了。

顺便再说个冷知识:这个紧凑字典的改动,除了顺序,还让 Python 3.6 的字典比 3.5 更省内存。有人实测过,相同数量的 key,3.6 的 dict 内存占用能少个 20%-25%,而查找速度基本不变。这在当时算是一个相当大的优化,特别是对 web 服务这种动不动要存几百万条数据的应用,能省不少钱。

如果你问我字典“无序变有序”的意义大不大,我会说:在日常写业务代码时,意义其实挺大。以前大家需要 OrderedDict 来保持顺序,现在用内置 dict 就够了,代码一下子清爽了不少。而且很多框架和库都受益,比如 JSON 的解析和序列化,或者数据库 ORM 的字段映射。以前结果顺序可能乱掉,现在就稳定多了。

但另一方面,这个顺序也不是银弹。有些场景下你依然需要专门的数据结构,比如 collections.OrderedDict 里有 move_to_endpopitem(last=False) 这种专门操作顺序的 API,普通 dict 是没有的。所以要记住:dict 有序只是遍历顺序固定,不是让它变成链表。

最后我想说,Python 的这种演进特别能体现一个哲学:语言规范和实现细节之间的平衡。3.6 给你了一个“看上去有序”的 dict,但那时候你不能依赖;等到 3.7 确认稳定了,才官方承诺。从“无序”到“有序”,这其实不是 Python 风格的转变,而是底层优化自然带来的红利。

所以,下次面试官再问你这个问题,你千万别只答一句“3.7 之后有序了”。你得顺着这个思路说出来:之前为什么无序(哈希表设计、语言规范不承诺),3.6 怎么改的(compact dict),3.7 为什么能写进规范(实现稳定,社区接受)。这样一来,不仅能展示你对 Python 的理解深度,还能让对方觉得你是真的懂底层,而不是背八股。

我自己有时候想啊,这种小小的语言改动,其实很能反映编程语言生态的走向:一门语言越成熟,越会考虑开发者的使用习惯。Python 的 dict 顺序保证,就是这么一个例子。从最初的“不保证”,到后来的“偶然保证”,再到最终的“官方保证”,这过程就像是和开发者的默契逐渐加深。你要说它是不是进步?我觉得是。至少写 Python 的我们,能少掉不少意外的 bug。

那我抛个问题给你们:既然 Python dict 现在有序了,你觉得未来 OrderedDict 会彻底被淘汰吗?

更多相关技术内容咨询欢迎前往并持续关注好学星城论坛了解详情。

想高效系统的学习Python编程语言,推荐大家关注一个微信公众号:Python编程学习圈。每天分享行业资讯、技术干货供大家阅读,关注即可免费领取整套Python入门到进阶的学习资料以及教程,感兴趣的小伙伴赶紧行动起来吧。

attachments-2022-05-rLS4AIF8628ee5f3b7e12.jpg

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
小柒
小柒

2184 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 2184 文章
  3. Pack 1351 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章