Create a total ordering from equality and one comparison
Also published in our Blogger archive.
A small value object often needs more than == and <: callers may also use !=, <=, >, and >=. @functools.total_ordering fills in the missing rich comparison methods when the class supplies __eq__() and one ordering method. Priority implements equality and __lt__() from its integer value, so the decorator can derive >= for the second assertion.
Both implemented methods return NotImplemented for a value that is not a Priority. That gives Python a chance to try the other operand's reflected method; if neither operand can order the pair, an ordering operation raises TypeError. Returning False there would incorrectly state that unlike types are comparable. Equality has different fallback behavior, so Priority(1) == 1 ultimately evaluates to False.
The printed list comes from sorted(), which uses the supplied less-than operation. This pattern is appropriate only when value does not change while an instance is being compared; mutating it can make ordering results inconsistent. total_ordering also adds method calls and more involved tracebacks, so a proven comparison hot spot may justify implementing every rich comparison directly.
AI assistance contributed to this Batu Lab Notes article.
from functools import total_ordering
@total_ordering
class Priority:
def __init__(self, value):
self.value = value
def __eq__(self, other):
if not isinstance(other, Priority):
return NotImplemented
return self.value == other.value
def __lt__(self, other):
if not isinstance(other, Priority):
return NotImplemented
return self.value < other.value
low = Priority(1)
high = Priority(3)
same_as_high = Priority(3)
assert low < high
assert high >= same_as_high
assert low != high
print(f"low < high: {low < high}")
print(f"high >= same: {high >= same_as_high}")
print([item.value for item in sorted([high, low, Priority(2)])])
Expected stdout:
low < high: True
high >= same: True
[1, 2, 3]