Which Python object comparison methods to redefine to make sorted() work?
Asked Answered
O

3

5

I feel this question must have been asked before but I could not find an answer.

Suppose I want to implement a Python class whose objects are sortable with sorted(). Do I have to reimplement all methods like __lt__(), __gt__(), etc.? What is the bare minimum? In other words, which method(s) does sorted() call for sorting?

Oviduct answered 29/6, 2021 at 5:54 Comment(0)
I
5

Per the documentation:

sort(*, key=None, reverse=False)
This method sorts the list in place, using only < comparisons between items. Exceptions are not suppressed - if any comparison operations fail, the entire sort operation will fail (and the list will likely be left in a partially modified state).

So you only need to define def __lt__(self,other): for your class.

Also see the Sorting HOW-TO which says near the bottom:

The sort routines are guaranteed to use __lt__() when making comparisons between two objects. So, it is easy to add a standard sort order to a class by defining an __lt__() method:

Incurvate answered 29/6, 2021 at 6:4 Comment(2)
But is sorted() implemented via sort()?Oviduct
@Oviduct The Python sort routines all use __lt__(). See edit for reference.Incurvate
P
3

There is a functools built-in method total_ordering that can decorate your class and enable its instances to be passed to sorted() without a key function specification.

The only requirements for the class is to define any of the comparison dunder methods and __eq__.

E.g.:

from functools import total_ordering

@total_ordering
class Sortable:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return self.x, self.y
    
    def __lt__(self, obj):
        return self.y < obj.y

    def __eq__(self, obj):
        return self.y == obj.y

obj_1 = Sortable("Hello", 9)
obj_2 = Sortable("World", -2)
obj_3 = Sortable("!", 5.5)
print(sorted([obj_1, obj_2, obj_3])

Which outputs:

>>> [("World", -2), ("!", 5.5), ("Hello", 9)]
Purse answered 29/6, 2021 at 6:46 Comment(0)
S
2

Only __lt__ is required. See the following example:

class MyCustomNumber:
    def __init__(self, some_number):
        self.some_number = some_number

    def __lt__(self, other):
        return self.some_number < other.some_number
    
list_ = [MyCustomNumber(1), MyCustomNumber(5), MyCustomNumber(-3), MyCustomNumber(150)]

for x in sorted(list_):
    print(x.some_number)

Outputs:

-3
1
5
150

Even sorted(list_, reverse=True) works with just __lt__ implemented.

Siloam answered 29/6, 2021 at 6:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.