带类型注解且默认值为None的数据类哈希值为何每次运行不一致?
问题解析
这个现象的核心原因在于Python 3.9/3.10版本中,dataclass在处理「带类型注解且默认值为None的字段」时,生成的__hash__方法存在异常,导致哈希值基于实例的内存地址(id)而非字段值。
具体分析三个类的行为:
Foo类:x和y均无类型注解,属于类属性而非dataclass实例字段。由于没有实例字段,dataclass不会生成自定义__hash__,继承自object的__hash__(基于实例id)。你设置了PYTHONHASHSEED=0,且每次运行时Foo()是第一个创建的对象,内存地址固定,因此哈希值稳定。Bar类:x有类型注解且默认值为1,是dataclass实例字段;y无类型注解,为类属性。frozen=True让dataclass生成基于字段值的__hash__方法,哈希值由x=1的元组哈希决定(hash((1,))),因此每次运行稳定。Foobar类:y有类型注解且默认值为None,是dataclass实例字段;x无类型注解,为类属性。本应生成基于y=None的元组哈希(hash((None,))),但Python 3.9/3.10的dataclass在此场景下未正确生成自定义__hash__,而是继承了object的__hash__。实例id每次运行时会变化(即使PYTHONHASHSEED=0,内存地址分配不固定),因此哈希值每次运行不同。
验证与修复
你可以手动为Foobar添加自定义__hash__方法来验证:
@dataclass(frozen=True) class Foobar: x = 1 y: Optional[int] = None def __hash__(self): return hash((self.y,))
此时运行脚本,Foobar()的哈希值会固定为hash((None,))的结果,不再随运行变化。
这个问题在Python 3.11及以上版本中已被修复,升级到更高版本即可解决该异常。
内容的提问来源于stack exchange,提问作者msailor
相关产品推荐
相关产品推荐

