Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Why does sort ignore the total ordering methods defined in my class?

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].

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading