Python 中什么对象可以作为字典的 key?tuple 一定可哈希吗?
下面是一段教学用的模拟面试。
🧑💻 面试官:Python 字典的 key 可以是什么对象?
🙋♂️ 我:必须是可哈希对象。
🧑💻 面试官:tuple 不可变,所以一定能当 key 吗?
🙋♂️ 我:如果里面包含 list,就不行。
🧑💻 面试官:自己定义一个类,让 hash 跟 name 有关。放进字典后再改 name,会发生什么?
可哈希不只是“能调用 hash”。它要求生命周期内哈希稳定,并且相等对象具有相同哈希值。
面试速答(60 秒版)
Python 的字典 key 和 set 元素需要可哈希。哈希值应在对象生命周期内稳定,而且两个对象相等时,必须有相同哈希值。
字符串、整数等常见不可变对象可以作为 key;list、dict、set 等可变容器通常不行。tuple 本身不可变,但只有内部元素也可哈希时,它才可哈希。
自定义对象不等于天然不可哈希,默认身份比较的普通实例通常可以。可是重写相等规则后,就必须认真设计 hash,不能依赖后来会改变的字段。
实际开发时,我更倾向用稳定 ID 或不可变值作为键。哈希相同不代表相等,字典还会处理冲突;反过来,相等却哈希不同则违反约定。

图:可哈希,需要稳定且相等一致。
知识点详解:为什么字典要求 key 的哈希稳定?
先算位置,再处理相等判断
假设字典用用户标识作为 key。查找时根据哈希缩小候选范围,再判断具体键是否匹配。
如果 key 插入后哈希变了,后来查找按新哈希去找,就可能无法找到原来的记录。因此关键约定是哈希稳定。
哈希也不是唯一编号。不同对象可能有相同哈希,这是正常冲突;实现还需要相等比较等机制。定义见 Python 可哈希术语。
tuple 为什么可能不可哈希?
tuple 不能替换自己的元素,但可以引用其他对象。tuple 中放一个 list,list 仍然是可变且不可哈希的。
因此,不能只检查外层类型名称。
Python
print(hash((1, "CN")))
try:
hash((1, ["CN"]))
except TypeError:
print("内部 list 不可哈希")
cache = {(7, "CN"): "value"}
print(cache[(7, "CN")])
本题只有 Python 示例。TypeScript/JavaScript Map 可以使用对象引用作为键,不要求用户提供 hash,并不是同语义翻译。

图:tuple 可哈希,还要看里面装什么。
可变对象,一定不能当 key 吗?
不能这样概括。普通自定义类沿用身份比较与对应哈希时,字段可变,不自动破坏它的身份键。
危险的是把相等和哈希定义在可变业务字段上。假设对象按 name 相等,hash 也按 name 计算,随后改 name,就破坏稳定性。
要区分“对象有可变字段”与“相等、哈希依赖的部分是否稳定”。
重写 eq,为什么可能变得不可哈希?
Python 对相等与哈希有配套规则。类重写 eq 却没有适当提供 hash 时,通常会被设为不可哈希,避免身份哈希与新值相等规则冲突。
确实需要值对象作为 key 时,采用稳定字段,让相等与哈希使用一致信息。规则见 Python 数据模型。
hash 值适合永久保存吗?
不适合把普通内置 hash 当跨进程、跨版本的稳定标识。某些类型的哈希还会受到进程随机化等行为影响。
保存长期身份应使用业务 ID;需要内容摘要时,明确指定算法与编码规则。不要把哈希表内部散列值当成持久化协议。
面试官继续追问
hash 相同,说明 key 一样吗?
不说明。相等要求相同哈希,但相同哈希不要求相等,这是单向约束。
frozenset 一定可哈希吗?
它的元素需要可哈希。不能把 frozen 理解成可以装任意对象再当键。
用字符串 ID 就完全没问题吗?
还要保证 ID 规范化与唯一性,但至少不把可变对象内部状态直接作为哈希依据。
面试速记卡
- 可哈希:哈希稳定,并能进行相等比较。
- 相等对象:必须具有相同哈希。
- tuple:内部元素也必须可哈希。
- 自定义类:相等与哈希一起设计。
- 键选择:优先稳定 ID 或真正不可变的值。
公司面试真题
这道题暂未收录可核验的公司真题来源。你可以先阅读本文解析,或浏览已收录的公司面试真题。
浏览公司面试真题 →