Python has allowed comparing objects since its early versions. To describe what "greater" or "equal" means for your objects, special comparison methods (eq, ne, lt, le, gt, ge) were created. With the introduction of PEP 207 (Python 2.1+), a unified comparison protocol was implemented, allowing programmers to explicitly control behavior during comparisons.
Standard types (int, str) are compared "by value", but user-defined classes are compared "by identity" (is) by default. If comparison magic methods are not implemented or are implemented incorrectly, comparing your objects will lead to unexpected behavior when working with collections, sorting, using set and dict, and may sometimes result in type errors.
You need to explicitly define comparison methods in your class. Typically, eq (equality) and lt (less than) are implemented, while the other methods can be automatically provided using the functools.total_ordering decorator.
Code example:
from functools import total_ordering @total_ordering class Point: def __init__(self, x, y): self.x = x self.y = y def __eq__(self, other): if not isinstance(other, Point): return NotImplemented return self.x == other.x and self.y == other.y def __lt__(self, other): if not isinstance(other, Point): return NotImplemented return (self.x, self.y) < (other.x, other.y) # Usage: p1 = Point(1, 2) p2 = Point(1, 3) print(p1 < p2) # True print(p1 == p2) # False
Key features:
Can you implement only one comparison method (eq or lt), and everything will work?
No. Implementing only eq will ensure correct equality comparison, but operators <, >, <=, >= will behave in the default way (typically through object identity comparison). To achieve full behavior, you should implement a sufficient set of methods or use functools.total_ordering.
Why doesn't compare(x, y) exist in Python, as in other languages?
Python inherited the idea of special "magic" methods for each operator, and the cmp function was removed starting from Python 3. For general object comparisons, rich comparison methods (lt, eq, etc.) are used, and for sorting, the key parameter in sort/sorted functions is used.
What happens if you compare an object with a type for which the required method isn't implemented?
If NotImplemented is returned in eq or other methods for unsupported types, Python will automatically try the reverse method on the second operand or return False. If such a case is not handled, a TypeError or incorrect behavior may occur.
Code example:
class Foo: def __eq__(self, other): return NotImplemented class Bar: pass print(Foo() == Bar()) # False (tries through Bar.__eq__)
A developer created a User class and implemented only eq without hash, so users were considered equal based on email. Then they tried to use User objects in a set/as dictionary keys.
Pros:
Cons:
A programmer uses @total_ordering and implements eq and lt with type checking, and also adds hash, if the class is immutable. Objects are compared correctly, sorted, and work as dictionary keys.
Pros:
Cons: