__eq__, __hash__, and Immutability
On this page
Rule: Hashable Implies Immutable
If an object is used as a dict key or set member, it must not change in a way that affects its hash or equality.
Correct eq/hash Pair
class UserKey:
def __init__(self, user_id: int) -> None:
self.user_id = user_id
def __eq__(self, other: object) -> bool:
if not isinstance(other, UserKey):
return NotImplemented
return self.user_id == other.user_id
def __hash__(self) -> int:
return hash(self.user_id)
Immutability Pattern
Prefer value objects (dataclass frozen, tuple fields, or no setters) for keys.
Operational Checklist
- If
__eq__is custom, consider whether__hash__is needed. - Do not mutate fields that participate in hashing after insertion into sets/dicts.
- Use immutable value objects for cache keys.
Failure Modes
- Key disappears: mutated key cannot be found in dict/set.
- Cache corruption: inconsistent hashing causes wrong lookups.