Python object equality: id vs value comparison
Demonstrates the difference between default identity comparison and custom equality, with a value-based class implementing __eq__ and __hash__.
Python code
33 linesimport copy
class IdOnly:
def __init__(self, name):
self.name = name
class ValueId:
def __init__(self, name):
self.name = name
def __eq__(self, other):
return isinstance(other, ValueId) and self.name == other.name
def __hash__(self):
return hash(self.name)
def __repr__(self):
return f"ValueId({self.name!r})"
if __name__ == "__main__":
a = copy.deepcopy(IdOnly("x"))
b = copy.deepcopy(IdOnly("x"))
print("id-only:", a == b, "| id(a) == id(b):", id(a) == id(b))
c = ValueId("x")
d = copy.deepcopy(ValueId("x"))
print("value-based:", c == d, "| id(c) == id(d):", id(c) == id(d))
e = ValueId("x")
print("hash consistency:", hash(c) == hash(e), "| dict lookup:", {c: "same value"} [ValueId("x")])
Output
id-only: False | id(a) == id(b): False
value-based: True | id(c) == id(d): False
hash consistency: True | dict lookup: same value
How it works
By default, Python compares objects by identity (is), not value. To make two separate objects with the same data compare equal, you override __eq__. When you do, you should also override __hash__ to keep objects usable as dict keys; two equal objects must have the same hash. copy.deepcopy creates distinct objects, so id-only comparison returns False even for identical data. The custom __eq__ checks type and field equality, making value-based comparison return True.
Common mistakes
- Forgetting to implement `__hash__` when overriding `__eq__`, breaking hash-based collections
- Comparing objects with `==` expecting value equality without defining `__eq__`
- Using mutable attributes in `__hash__` can lead to inconsistent hashing
Variations
- Use `functools.total_ordering` to auto-fill comparison operators from `__eq__` and `__lt__`
- Implement `__eq__` with a base class to share equality logic across subclasses
Real-world use cases
- Custom classes used as dict keys, like caching objects by their data
- Domain models where two records with identical fields should be considered the same
- Test assertions that compare value objects regardless of memory location
Sponsored
More from OOP & classes
- Add property getter setter validation in Python easy
- Binary Tree Inorder Traversal in Python easy
- Borg pattern shared state in Python medium
- Bridge Pattern in Python: Separate Abstraction from Implementation medium
- Composable Predicates with the &, |, ~ Operators in Python medium
- Composition over Inheritance: How to Build a Wallet Account in Python easy
Keep learning
Related tutorials and quizzes for this topic.