Python中可变数据类型生成的hashcode为什么不能作为字典的key?
为什么字典不能自动更新key的hash值
你之前对字典哈希机制的理解完全正确,「自动更新key的hash值」这个方案不可行的核心原因有四个:
- 感知成本过高:字典没有办法主动感知到作为key的可变对象发生了变更。比如你用列表
a = [1,2]作为key存入字典后,执行a.append(3)修改列表内容时,列表本身不会主动通知所有引用了它作为key的字典,字典自然没有触发更新hash的时机。如果要实现通知机制,就得让每个可变对象维护所有引用它的字典的列表,一个可变对象如果被几十上百个字典用作key,单次修改就要同步更新几十上百个字典的哈希表,性能开销完全无法接受。 - 会引发大量稳定性问题:字典的哈希表是按hash值取模后把键值对分到不同的存储桶里,key的hash值变化后,对应的存储桶也会变化,需要把旧桶里的键值对删除再插入新桶。这个过程会修改字典的底层结构,不仅会导致多线程环境下必须加额外的锁才能避免数据错乱,还会让字典迭代器极易失效——迭代字典的过程中如果某个key变更触发了哈希表重排,整个迭代过程直接崩溃,这类问题几乎无法调试。
- 会出现不可预期的键覆盖:字典定位key的逻辑是「先匹配hash值,再判断两个key是否相等」。如果允许key的hash动态更新,完全可能出现两个原本不同的key,修改后hash值和相等性都一致的情况。比如先把
a = [1]、b = [2]两个列表作为key存入字典,之后把b修改为[1],此时a和b的hash相同、互相相等,按字典规则同一个key不能重复,更新b的hash时到底是覆盖a的条目还是保留两个,无论选哪种都会让开发者拿到不符合预期的结果。 - 不符合Python的底层设计契约:Python从设计之初就明确了「作为字典key的对象,其哈希值在生命周期内必须不可变」的契约,所有字典操作的性能优化都是基于这个契约做的。如果要加自动更新hash的逻辑,就要给所有可变类型加额外的监听逻辑,所有字典操作都要加兼容逻辑,会让所有使用字典的场景性能都下降,为了一个极少出现的小众需求牺牲全局性能,完全得不偿失。
如果确实需要用可变内容作为key,完全可以手动把可变对象转成不可变类型再用,比如把列表转成元组,或者自己实现自定义类管控哈希值的计算逻辑,成本比修改底层字典机制低得多。
内容的提问来源于stack exchange,提问作者young_minds1
相关产品推荐
相关产品推荐

