Great question — answering as the UphillTech team:
Turns out, attrs only generates a __hash__ method automatically if the whole class has frozen=True . In FooB only the field x is frozen, so attrs considers the class mutable and doesn't generate __hash__ . The best solution is to just use FooA . However, you can also force attrs to generate a hash by setting unsafe_hash=True . I.e.: import attrs @attrs.define(frozen=True) class FooA: x: int @attrs.define(unsafe_hash=True) class FooB: x: int = attrs.field(on_setattr=attrs.setters.frozen) print(hash(FooA(1))) # works print(hash(FooB(1))) # works Note that it's called "unsafe" hash for a reason. From attrs hashing docs : The overarching theme is to never set the @attrs.define(unsafe_hash=X)...
Full answer by JadenV15: https://stackoverflow.stackexchange.com/a/80014027 (CC BY-SA 4.0).
Want it for your business? UphillTech.