Object of custom type as dictionary key
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
In Python, a custom object can be used as a dictionary key, but only if it behaves like a stable, hashable value. The important rule is not "custom objects are forbidden," but "dictionary keys must have consistent equality and hash behavior."
What a Dictionary Needs From a Key
Python dictionaries use a hash table internally. For an object to work as a key, two conditions have to stay true:
- the hash value must not change while the object is in the dictionary
- objects that compare equal must return the same hash
If either rule is violated, lookups become unreliable.
A Good Modern Pattern: dataclass(frozen=True)
For value-like objects, the easiest solution is often a frozen data class:
This works well because the object is immutable and Python can safely use its field values for equality and hashing.
Manual __eq__ and __hash__
If you are not using dataclass, you can implement the behavior yourself:
The tuple inside __hash__ is a common technique because tuples are already hashable when their contents are hashable.
Why Mutability Is Dangerous
The most important design rule is that the data used for equality and hashing should not change after insertion. Consider this bad example:
After the mutation, the dictionary may no longer find the entry the way you expect. The object is still there, but you broke the contract that the hash-relevant state stays stable.
Identity Keys vs Value Keys
By default, a plain user-defined object uses identity-based behavior inherited from object. That means two instances with the same field values are treated as different keys unless you override equality and hashing.
That default is not wrong. It just answers a different question. Use identity-based keys when object identity matters. Override the behavior only when the object should act like a value.
Choosing Fields for the Hash
Only include fields that define the key's identity. If an attribute is descriptive but not part of uniqueness, leave it out.
For example, a user key might depend on tenant_id and username, but not on last_login_time. If you include changing descriptive fields in the hash, you create unnecessary instability and surprising dictionary behavior.
Testing Your Design
A simple sanity test is:
If both lines do not behave as expected, the object is not ready to be a dictionary key.
Common Pitfalls
The most common mistake is implementing __eq__ but forgetting __hash__. In Python, that often makes the class unhashable because Python disables hashing when equality is customized without a matching hash definition.
Another problem is using mutable fields inside the hash. Even if the code seems to work at first, later mutations can break lookups in subtle ways.
People also overcomplicate the implementation. If the class is just a value container, @dataclass(frozen=True) is usually cleaner than hand-written boilerplate.
Summary
- Custom Python objects can be dictionary keys if they are hashable and equality is well defined.
- Equal objects must produce the same hash value.
- The fields used for hashing must remain stable while the key is in the dictionary.
- '
@dataclass(frozen=True)is often the simplest safe solution.' - Use identity-based keys only when object identity, not field values, is the real key.

