如何使一个Python数据类继承__hash__?

20
下面的代码可以运行,但我不想在每个子类中重复__hash__。有没有办法告诉数据类继承哈希函数(即不将其设置为None)?
from dataclasses import dataclass


@dataclass
class Hashable:

    def __hash__(self):
        hashed = hash((
            getattr(self, key)
            for key in self.__annotations__
            ))
        return hashed


@dataclass
class Node(Hashable):
    name: str = 'Undefined'

    def __hash__(self):
        return Hashable.__hash__(self)

1
你有特别的原因要自己定义哈希表吗?这基本上是一个更容易出错、不太安全的版本,如果你设置frozen=True,就可以得到相同的结果。 - user2357112
1个回答

18
您的 __hash__ 被设置为 None 的原因是,dataclasses试图防止您自己绊倒自己。您的第二个类在数据类装饰器中具有 eq=True(这是默认值)。从文档中可以看出:
“以下是隐式创建 __hash__() 方法的规则。请注意,您不能在数据类中既有显式的 __hash__() 方法,又将 unsafe_hash=True; 这将导致 TypeError 。”
“如果 eq 和 frozen 都为True,则默认情况下 dataclass() 会为您生成一个 __hash__() 方法。如果 eq 为 True 而 frozen 为 False,则 __hash__() 将被设置为 None,标记为不可哈希(由于它是可变的,因此是不可哈希的)。如果 eq 为 false,则 __hash__() 方法将保持不变,这意味着将使用超类的 __hash__() 方法(如果超类是object,则这意味着它将退回到基于id的哈希)。 ”
所以只需传递 eq=False:
In [1]: from dataclasses import dataclass
   ...:
   ...:
   ...: @dataclass
   ...: class Hashable:
   ...:
   ...:     def __hash__(self):
   ...:         hashed = hash((
   ...:             getattr(self, key)
   ...:             for key in self.__annotations__
   ...:             ))
   ...:         return hashed
   ...:
   ...:
   ...: @dataclass(eq=False)
   ...: class Node(Hashable):
   ...:     name: str = 'Undefined'
   ...:

In [2]: hash(Node())
Out[2]: -9223372036579626267

然而,正如评论中指出的那样,这并不是非常安全的,因为您现在有一个可变对象,它现在是可哈希的,但与其继承自Hashable__eq__实现不一致。


1
我认为真正的问题在于他们定义了这个__hash__,而不是使用frozen=True - user2357112
@user2357112 我同意。 - juanpa.arrivillaga
1
@user2357112 这个例子必须保持简单。假设frozen是我使用情况的好主意是一个错误的假设。 - Brian Bruggeman
@BrianBruggeman:嗯,你的__hash__示例有漏洞(不正确地处理了ClassVarInitVar和继承字段),如果你特别不想要frozen=True,那么这听起来像是你想要可变哈希对象,这通常是一个坏主意。即使你不想要frozen=Trueunsafe_hash=True也可能是一个更好的选择。 - user2357112
1
Unsafe 只处理简单的原始类型,无法处理更复杂的数据。我想要一个可重用的函数,既可以测试,也可以传递给子类链。使用 dataclass(eq=False) 实际上比我的原始示例更加混淆/不够清晰。 - Brian Bruggeman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接