Mastery
Mastery/Python/D. Classes & OOP internals
T1 · high-leverage
Compare with Java: The equality/hash-code contract for hash-based collections

The __eq__/__hash__ contract

If a class defines __eq__, Python sets __hash__ to None automatically unless you also define it -- this is stricter than many languages: the object doesn't silently misbehave in sets/dicts, it becomes outright unhashable and hash(obj) raises TypeError. This is the language enforcing the contract ("equal objects must have equal hashes") at the point of misuse, rather than letting it corrupt a hash table silently.

python
class Bad:
    def __init__(self, v):
        self.v = v
    def __eq__(self, other):
        return isinstance(other, Bad) and self.v == other.v
    # no __hash__ override

b = Bad(1)
try:
    hash(b)
except TypeError as e:
    print("hash() on an eq-only class:", e)
print("Bad.__hash__ is:", Bad.__hash__)

try:
    {b}
except TypeError as e:
    print("can't even put it in a set:", e)

Defining both correctly makes the class usable in hash-based collections again -- and the hash MUST be derived from the same fields __eq__ compares, or equal objects could land in different buckets:

python
class Good:
    def __init__(self, v):
        self.v = v
    def __eq__(self, other):
        return isinstance(other, Good) and self.v == other.v
    def __hash__(self):
        return hash(self.v)   # same field(s) as __eq__ uses

s = {Good(1)}
print("Good(1) in {Good(1)}:", Good(1) in s, " <- found, because equal hash AND equal value")

Interview angle

A pointed follow-up to "how do you make a class usable as a dict key or set member?" — most candidates know to override __eq__, far fewer know Python then silently sets __hash__ to None unless you also define it. That's a great signal question because the failure mode is a hard TypeError, not a subtle bug, so a candidate who's actually hit it remembers it vividly.

In the industry

This is Python's language design actively preventing the equivalent Java bug (topic-linked: Java's hashCode/equals contract can be silently violated) — by refusing to let an eq-only class be hashed at all, rather than letting it corrupt a set/dict at runtime. In practice this surfaces immediately (and loudly) the first time someone tries to put such an object in a set, which is exactly why this class of bug is rarer in Python codebases than in Java ones with hand-written equals.