I am running into some unexpected behavior when trying to hash a dataclass and I'm wondering if anyone can explain it.
The below script reproduces the problem. First, we need to run export PYTHONHASHSEED='0' to disable hash randomization so we can compare the hash across runs.
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()))
Here's the result of running the script twice:
>>> py temp.py
hash(Foo()): 5740354900026072187
hash(Bar()): -6644214454873602895
hash(Foobar()): 582415153292506125
>>> py temp.py
hash(Foo()): 5740354900026072187
hash(Bar()): -6644214454873602895
hash(Foobar()): -8226650923609135754
Note that the hash for the first two classes is the same across runs, but the hash of the last class is different each time. It seems to be the combination of the type annotation with the value None in the class Foobar that causes the hash to change. (Incidentally, if I replace Optional[int] with int I get the same behavior.)
I tried with both Python 3.9 and 3.10 and got similar results each time.
Can anyone explain what is going on?