Question presented to candidate:
"If I write a class and want to use its instances as dictionary keys or put them in a set, what do __eq__ and __hash__ have to do, and what goes wrong if they disagree?"
What a strong answer should cover:
- The contract: if
a == bthenhash(a) == hash(b); the reverse is not required (collisions are allowed). A hash must also stay the same for the object's whole life while it is in a set or dict. - Defaults: plain objects compare and hash by identity. Defining
__eq__without__hash__makes Python 3 set__hash__ = None, so instances become unhashable (TypeError: unhashable type). - How dicts and sets use it: hash picks the slot, then identity is checked, then
__eq__; breaking the contract gives silent duplicates or lookups that miss. - Implement both from the same immutable fields:
__eq__compares a tuple of fields (returningNotImplementedfor other types),__hash__returnshash()of the same tuple; mutable objects should not be hashable. - Tools and details:
@dataclass(frozen=True)generates both,eq=Truealone removes the hash,unsafe_hash=Trueis risky;1 == 1.0 == Trueshare a hash; string hashes are randomised per process (default since 3.3).
Clarifying questions expected:
- Will the objects be mutated after they are added to a set or used as keys?
Code / implementation expected: Yes β a value class with consistent __eq__/__hash__, and a demonstration of what breaks when the contract is violated or a key is mutated.