Given the following class:
@functools.total_ordering
class Entry:
def __init__(self, nr: list[int, int] = None, p: int = 0) -> None:
self.nr = nr if nr is not None else [0, 0]
self.p = p
def __repr__(self) -> str:
return f"Entry(nr={self.nr}, p={self.p})"
def __eq__(self, other: Entry) -> bool:
return (self.nr[0] == other.nr[0] and self.nr[1] >= other.nr[1]) or (self.nr[0] > other.nr[0])
def __gt__(self, other: Entry) -> bool:
return (self.nr[0] == other.nr[0] and self.nr[1] < other.nr[1]) or (self.nr[0] < other.nr[0])
And a list of entries:
L = [
Entry(nr=[98, 111], p=0),
Entry(nr=[111, 98], p=1),
Entry(nr=[98, 111], p=2),
Entry(nr=[111, 99], p=3),
Entry(nr=[99, 101], p=4),
Entry(nr=[101, 108], p=5),
Entry(nr=[108, -1], p=6)
]
Calling L.sort() is expected to produce the following ordering (only p values shown for brevity): [0, 2, 4, 5, 6, 1, 3].
But nothing happens! Why not?
I have also experimented with making the class a dataclass by replacing the __init__ with the following (and adding a dataclass annotation to the class, of course), but that didn’t change anything. I’d prefer it to be a dataclass, so, that I don’t have to provide an implementation of __repr__.
nr: list[int, int] = dataclasses.field(default_factory=lambda: [0, 0])
p: int = 0
>Solution :
As far as I can tell it’s just a logic error in your comparator methods — note that __eq__ is for ==, and __gt__ is for <.
Your __eq__ is implemented more like a <= operator, which means it’s not testing for equality in any conventional sense, and is in fact non-commutative, which you should expect to produce some weird behavior (including stuff like sort not doing anything because it thinks all the items are equivalent).
If you just do:
def __eq__(self, other: 'Entry') -> bool:
return (self.nr == other.nr)
def __gt__(self, other: 'Entry') -> bool:
return (self.nr > other.nr)
then __eq__ has more useful behavior, functools.total_ordering is able to fill in the rest of the methods, and you get exactly the sorting behavior you’re looking for.