繁体   English   中英

具有类型注释和默认值的字段的数据类 hash() = None 始终是不确定的

[英]dataclass hash() of field with type annotation and default value = None is always nondeterministic

我在尝试 hash 数据类时遇到了一些意外行为,我想知道是否有人可以解释它。

下面的脚本重现了该问题。 首先,我们需要运行export PYTHONHASHSEED='0'来禁用 hash 随机化,这样我们就可以比较 hash 的运行情况。

import os
from dataclasses import dataclass
from typing import Optional

assert os.getenv("PYTHONHASHSEED", None) == "0"


@dataclass(frozen=True)
class Foo:
    x = 1
    y = None


@dataclass(frozen=True)
class Bar:
    x: Optional[int] = 1
    y = None


@dataclass(frozen=True)
class Foobar:
    x = 1
    y: Optional[int] = None


print("hash(Foo()):", hash(Foo()))
print("hash(Bar()):", hash(Bar()))
print("hash(Foobar()):", hash(Foobar()))

这是两次运行脚本的结果:

>>> py temp.py 
hash(Foo()): 5740354900026072187
hash(Bar()): -6644214454873602895
hash(Foobar()): 582415153292506125
>>> py temp.py 
hash(Foo()): 5740354900026072187
hash(Bar()): -6644214454873602895
hash(Foobar()): -8226650923609135754

请注意,前两个类的 hash 在运行中是相同的,但最后一个 class 的 hash 每次都不同。 似乎是类型注释与 class Foobar中的值 None 的组合导致 hash 发生变化。 (顺便说一句,如果我用int替换Optional[int]我会得到相同的行为。)

我尝试了 Python 3.9 和 3.10,每次都得到类似的结果。

谁能解释发生了什么?

必须对数据类字段进行注释。 注释是数据类机制如何确定某事是一个字段。 由于缺少注释,您的所有 3 个数据类都已损坏。


禁用 hash 随机化不应该使哈希具有确定性。 它只是禁用了一项特定的安全功能,该功能故意随机化某些类型的哈希以减轻 hash 基于冲突的拒绝服务攻击。

默认的 CPython object.__hash__是不确定的。 它基于对象的地址,每次运行都不一致。 None使用此默认 hash,因此hash(None)是不确定的,并且您的数据类哈希基于其字段的哈希,因此具有None字段值的数据类的 hash 也是不确定的。 但是,由于您的数据类已损坏,因此Foobar是唯一一个y实际上是字段的。

Bar()的 hash 似乎是确定性的,因为它只依赖于整数和元组的哈希(冻结的数据类__hash__ 实现构建了一个字段值和哈希的元组),而整数和元组 hash 恰好接近算法确定性的。 不过,它们实际上并不是确定性的。 它们取决于您使用的是 32 位还是 64 位 Python 构建,虽然主要指定了 int 散列算法,但元组 hash 算法是所有实现细节。

无论您使用什么设置, hash的设计都不是确定性的。 如果您需要确定性哈希,请不要使用hash

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM