Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python dataclass generate hash and exclude unsafe fields

I have this dataclass:

from dataclasses import dataclass, field
from typing import List

@dataclass
class Person:
    name: str
    dob: str
    friends: List['Person'] = field(default_factory=list, init=False)

name and dob are immutable and friends is mutable. I want to generate a hash of each person object. Can I somehow specify which field to be included and excluded for generating the __hash__ method? In this case, name and dob should be included in generating the hash and friends shouldn't. This is my attempt but it doesn't work

@dataclass
class Person:
    name: str = field(hash=True)
    dob: str = field(hash=True)
    friends: List['Person'] = field(default_factory=list, init=False, hash=False)
>>> hash(Person("Mike", "01/01/1900"))
Traceback (most recent call last):
  File "<pyshell#43>", line 1, in <module>
    hash(Person("Mike", "01/01/1900"))
TypeError: unhashable type: 'Person'

I also can't find a way to set name and dob to be frozen. And I'd refrain from setting unsafe_hash to True, just by the sound of it. Any suggestions?

Also, is what I'm doing considered good practice? If not, can you suggest some alternatives?

Thank you

Edit: This is just a toy example and we can assume that the name and dob fields are unique.

Edit: I gave an example to demonstrate the error.

like image 969
Mike Pham Avatar asked Jun 17 '26 02:06

Mike Pham


1 Answers

Just indicate that the friends field should not be taken in account when comparing instances with __eq__, and pass hash=True to field instances on the desired fields.

Then, pass the unsafe_hash=True argument to the dataclass decorator itself - it will work as you intend (mostly):

In case of hash, the language restriction is that if one instance compares equal with another (__eq__), the hash of of both must be equal as well. The implication in this case is that if you have two instances of the "same" person with the same "name" and "dob" fields, they will be considered equal, even if they feature different friends lists.

Other than that, this should work:


from dataclasses import dataclass, field
from typing import List

@dataclass(unsafe_hash=True)
class Person:
    name: str = field(hash=True)
    dob: str = field(hash=True)
    friends: List['Person'] = field(default_factory=list, init=False, compare=False, hash=False)

Then, remember to behave like a "consenting adult" and not change the "name" and "dob" fields of Person instances in any place, and you are set.

like image 96
jsbueno Avatar answered Jun 18 '26 17:06

jsbueno



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!