为何Python 2仅对旧式类禁用隐式__hash__方法?
__eq__时的哈希行为差异原因 在任何语言中重写类的相等性方法时,都需注意避免创建与相等性逻辑不一致的哈希函数——这是字典、集合等哈希表结构能正常工作的核心前提。
Python 3与Python 2旧式类的行为
当仅重写__eq__方法时,Python 3和Python 2的旧式类会自动移除__hash__方法,此时类的实例无法作为字典的键或存入集合,直接使用会抛出错误:
class C: def __eq__(self, o): return "hello" {C() : 1} # 抛出错误
Python 2新式类的行为
但Python 2中继承自object的新式类,却允许此类哈希操作:
class A(object): def __eq__(self, o): return "hello" {A() : 2} # 可正常执行
这两种差异带来两个意外点:
- 此前认为Python 3类与Python 2新式类行为一致的认知有误
- Python 2引入新式类时似乎移除了一项安全特性
差异背后的原因
这个行为差异是Python语言演化中,对哈希与相等性一致性规则逐步严格化的结果:
Python 2新式类的兼容性妥协
Python 2推出新式类时,为了兼容大量旧代码,没有强制绑定__eq__与__hash__的逻辑。它默认沿用object类的__hash__实现——基于对象内存地址生成哈希值,哪怕用户重写了__eq__。这种设计给了开发者灵活性,但也埋下隐患:如果两个对象通过__eq__判断为相等,却因为内存地址不同拥有不同哈希值,会直接破坏哈希表的一致性,导致字典、集合出现查找失效、数据丢失等异常。Python 3的规则收紧
到Python 3阶段,社区意识到这种灵活性带来的大量隐性bug,于是统一了规则:只要重写__eq__但未同步重写__hash__,Python会自动将该类的__hash__设为None,直接禁止哈希操作。这是一种强制的安全机制,目的是明确提醒开发者:修改相等性逻辑后,必须同步调整哈希函数,确保相等的对象一定有相同哈希值,从根源避免哈希表的异常行为。
而Python 2的旧式类之所以和Python 3行为一致,是因为旧式类沿用了早期Python的严格规则,新式类作为当时的新特性为兼容做了妥协,最终在Python 3中废弃旧式类,统一了更严谨的规范。
内容的提问来源于stack exchange,提问作者julaine

