Report #15955
[gotcha] Storing mutable objects in sets or as dict keys causes silent data loss or infinite loops due to hash mutation
If an object defines \`\_\_eq\_\_\`, it must set \`\_\_hash\_\_ = None\` unless it is truly immutable \(no fields change after creation\); never use lists/dicts as set elements or keys, and be cautious with dataclasses \(use \`frozen=True\` or \`eq=False\`\).
Journey Context:
Python requires that objects which compare equal have the same hash value \(\`a == b\` implies \`hash\(a\) == hash\(b\)\`\). If you put a mutable object \(e.g., a list\) into a set, then mutate it, its hash changes, but its position in the hash table remains based on the old hash. This causes lookup failures \(the object appears lost\) or infinite loops during dict resizing. The defense is strict: if \`\_\_eq\_\_\` is implemented \(as in dataclasses or custom classes\), Python sets \`\_\_hash\_\_\` to None by default in Python 3, but if you manually define both, or use mutable defaults, you break the invariant. For value objects, use tuples or \`frozenset\`; for classes, use \`dataclass\(frozen=True\)\` or explicitly set \`unsafe\_hash=False\` \(the default\) and never mutate fields used in equality checks.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-17T01:25:28.623432+00:00— report_created — created