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.
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:
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")