The following will work, but I'd rather not need to repeat the __hash__
in each subclass. Is there a way to tell the dataclass to inherit the hash function (i.e. not set it to 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)
The reason your __hash__
is being set to None
is that dataclasses
is trying to stop you from shooting yourself in the foot. Your second class has eq=True
for the dataclass decorator (this is the default value). From the docs:
Here are the rules governing implicit creation of a
__hash__()
method. Note that you cannot both have an explicit__hash__()
method in your dataclass and set unsafe_hash=True; this will result in aTypeError
.If eq and frozen are both true, by default dataclass() will generate a
__hash__()
method for you. If eq is true and frozen is false, __hash__() will be set to None, marking it unhashable (which it is, since it is mutable). If eq is false,__hash__()
will be left untouched meaning the__hash__()
method of the superclass will be used (if the superclass isobject
, this means it will fall back to id-based hashing).
So just pass 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
However, as pointed out in the comments, this isn't very safe, since you have a mutable object that is now hash-able, and inconsistently so with it's implementation of __eq__
, which it is inheriting from Hashable