Reputation: 2040
I have a Python class like this:
@dataclass
class Person:
name: str
relatives: Iterable['Person']
I want to use the name
attribute for all comparisons, so that Person
objects are sorted alphabetically by default. Hence, I override the __gt__()
method like this (as described in the documentation):
def __gt__(self, other):
return self.name > other.name
This actually resolves my initial task, I can do e.g.:
p1=Person("test1", relatives=[])
p2=Person("test2", relatives=[])
sorted([p1, p2])
Out[4]: [Person(name='test1', relatives=[]), Person(name='test2', relatives=[])]
As well as this:
p1>p2
Out[5]: False
p1<p2
Out[6]: True
I understand that I don't have to implement all operators:
__lt__() and __gt__() are each other’s reflection, __le__() and __ge__() are each other’s reflection, and __eq__() and __ne__() are their own reflection.
For equality comparison, however, I still have to override __eq__(other)
explicitly:
def __eq__(self, other):
return isinstance(other, Person) and self.name == other.name
Is there a way to implement this in a more concise way? I would envision some way to tell the interpreter 'use attribute name
for all comparisons!'.
Upvotes: 1
Views: 218
Reputation: 36680
If providing __eq__
and at least 1 of rich comparison is okay in your case you might use functools.total_ordering decorator, like so:
import functools
@functools.total_ordering
class Name:
def __init__(self, name):
self.name = name
def __repr__(self):
return self.name
def __eq__(self, other):
return self.name == other.name
def __gt__(self, other):
return self.name > other.name
a = Name("a")
b = Name("b")
print(a<b)
print(a<=b)
print(b>=a)
output
True
True
True
If this is not accepable in your case you would need to prepare own decorator to use, for discussion of decorating class see How to decorate a class
Upvotes: 4