Report #7464
[gotcha] Subclass overriding \_\_eq\_\_ inherits \_\_hash\_\_ from parent causing dict lookup failures
Whenever you override \_\_eq\_\_ in a subclass, explicitly declare \_\_hash\_\_ = None in the class body to make the class unhashable, or implement a custom \_\_hash\_\_ based strictly on immutable fields used in \_\_eq\_\_. Never rely on implicit inheritance of \_\_hash\_\_ from parent classes when \_\_eq\_\_ is overridden.
Journey Context:
Python's object model requires that if a == b, then hash\(a\) == hash\(b\). This is fundamental to how dictionaries and sets achieve O\(1\) lookup. When you override \_\_eq\_\_ to compare objects by value rather than identity, you must ensure the hash function reflects this new equality semantics. The footgun arises because of a subtle rule: if you define \_\_eq\_\_ in a class, Python automatically sets \_\_hash\_\_ to None for that class. However, if you inherit \_\_eq\_\_ \(from a parent\) and don't define it yourself, or if you define \_\_eq\_\_ in a child of a parent that had \_\_hash\_\_, the child inherits the parent's \_\_hash\_\_ implementation. This creates a situation where a subclass has a custom equality \(say, comparing by name\) but uses the parent's identity-based hash \(by id\). This causes dict lookups to fail silently \(objects appear not to be in the dict even when they are equal\) or sets to contain duplicates. The 'fix' of setting \_\_hash\_\_ = None makes the class unhashable, which is usually the safest default for mutable value objects.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T02:46:01.514266+00:00— report_created — created