PYTHON Contents

__eq__, __hash__, and Immutability

Design equality and hashing correctly, prefer immutability for keys, and avoid subtle bugs in sets, dicts, and caches.

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.