Tworzenie klasy zdefiniowanej przez użytkownika w języku Python z możliwością sortowania i mieszania

83

Jakie metody należy przesłonić / zaimplementować podczas tworzenia klas zdefiniowanych przez użytkownika, które można sortować i / lub haszować w Pythonie?

Na co trzeba uważać?

Wpisuję dir({})do mojego interpretera, aby uzyskać listę metod wbudowanych w dykty. Spośród nich zakładam, że muszę zaimplementować jakiś podzbiór plików

['__cmp__', '__eq__', '__ge__', '__gt__', '__hash__', '__le__', '__lt__', '__ne__']

Czy istnieje różnica w tym, które metody muszą zostać zaimplementowane w Pythonie3, a nie w Pythonie2?

Matt Fenwick
źródło
3
Dobra dyskusja tutaj: stackoverflow.com/q/1061283/641766 . Różnica między Pythonem 2.xi 3.x polega na tym, że __cmp__został usunięty.
zeekay

Odpowiedzi:

90

Prawie opublikowałem to jako komentarz do innych odpowiedzi, ale tak naprawdę jest to odpowiedź sama w sobie.

Aby Twoje przedmioty można było sortować, wystarczy je zaimplementować __lt__. To jedyna metoda używana przez sortowanie wbudowane.

Inne porównania lub functools.total_orderingsą potrzebne tylko wtedy, gdy faktycznie chcesz użyć operatorów porównania w swojej klasie.

Aby umożliwić haszowanie elementów, implementujesz je __hash__tak, jak zauważyli inni. Powinieneś także implementować __eq__w sposób zgodny - elementy, które są równoważne, powinny haszować to samo.

agf
źródło
więc zła implementacja __lt__może spowodować nieprzewidywalne sortowanie Pythona? (na przykład, jeśli x .__ lt __ (y) i y .__ lt __ (x))
Matt Fenwick
3
Nie wiem, czy coś jest „nieprzewidywalne”, będzie spójne, jeśli zostanie podane dokładnie to samo wejście, ale inna kolejność wprowadzania może spowodować, że różne elementy będą w innej kolejności. Tak, jeśli nieprawidłowo zaimplementujesz porównanie użyte do sortowania, Python będzie sortował nieprawidłowo. Poleciłbym __key__funkcję, która zamienia instancję w krotkę, a następnie po prostu użyj obu __lt__( self.__key__() < other.__key__()) i __hash__( hash(self.__key__())).
agf
21

Nie ma żadnej różnicy między Pythonem 2 i 3.

Do sortowania:

Należy zdefiniować metody porównawcze. Dzięki temu Twoje przedmioty można sortować. Generalnie nie powinieneś preferować __cmp__().

Zwykle używam dekoratora functools.total_ordering.

functools.total_ordering (cls) W przypadku klasy definiującej jedną lub więcej rozbudowanych metod porządkowania porównań resztę dostarcza dekorator tej klasy. Upraszcza to wysiłek związany z określeniem wszystkich możliwych bogatych operacji porównania:

Klasa musi określić jeden __lt__(), __le__(), __gt__(), lub __ge__(). Ponadto klasa powinna zawierać __eq__()metodę.

Należy uważać, aby metody porównawcze nie miały żadnych skutków ubocznych. (zmień dowolną wartość obiektu)

Do haszowania:

Powinieneś zaimplementować __hash__()metodę. Myślę, że najlepszym sposobem jest powrót hash(repr(self)), więc Twój haszysz byłby wyjątkowy.

utdemir
źródło
Aby functools.total_orderingzapoznać się z przykładem z dokumentacji, zobacz tutaj .
Evgeni Sergeev
3

Istnieje kilka sposobów oznaczenia obiektu jako możliwego do sortowania. Po pierwsze - bogate porównanie, zdefiniowane przez zestaw funkcji:

object.__lt__(self, other)
object.__le__(self, other)
object.__eq__(self, other)
object.__ne__(self, other)
object.__gt__(self, other)
object.__ge__(self, other)

Możliwe jest również zdefiniowanie tylko jednej funkcji:

object.__cmp__(self, other)

A ostatnia powinna zostać zdefiniowana, jeśli chcesz zdefiniować __hash__funkcję niestandardową . Zobacz doc .

Roman Bodnarchuk
źródło
6
W Pythonie 3 „[...] __cmp__()metoda specjalna nie jest już obsługiwana”, zobacz odpowiednią sekcję tutaj .
Evgeni Sergeev
-3

__lt__(self,other)Metoda implementacji jest odpowiedzią, która umożliwia sortowanie klasy.
Może być używany nie tylko do metody wbudowanej sorted(iterable), ale także do kolejkowania priorytetów przez heapqmoduł.

Ponadto nie podoba mi się projekt Pythona, więc wiele '__ge__', '__gt__', '__le__', '__lt__', '__ne__'metod nie jest w ogóle intuicyjnych !
Dla kontrastu, Java Interface Comparable<T> (patrz dokumentacja java ) zwraca ujemną liczbę całkowitą, zero lub dodatnią liczbę całkowitą, ponieważ ten obiekt jest mniejszy, równy lub większy niż określony obiekt, co jest bezpośrednie i przyjazne !

yichudu
źródło
9
Większość odpowiedzi obejmuje Twoją opinię (która nie powinna być częścią odpowiedzi).
jazda na nartach
@skyking ... chociaż nie zgadzam się z opiniami zawartymi w tej konkretnej odpowiedzi, uważam, że opinia (zawierająca zbadane i przydatne dane odnoszące się do pytania) jest bardzo cenna. Błędem w tej odpowiedzi opinii jest brak poparcia opinii odpowiednimi danymi. Ale zrozumienie preferencji pomaga ludziom podejmować decyzje i dlatego jest bardzo przydatne. Tutaj odpowiedź jest źle sformułowana, ponieważ nie ma użytecznego i wykonalnego kodu Pythona, który zilustrowałby pythoniczny sposób tworzenia kodu w oparciu o preferencje autora.
Andrew