Eine benutzerdefinierte Python-Klasse sortierbar und hashbar machen

82

Welche Methoden müssen überschrieben / implementiert werden, wenn benutzerdefinierte Klassen in Python sortierbar und / oder hashbar gemacht werden sollen?

Worauf sollten Sie achten?

Ich tippe dir({})in meinen Interpreter, um eine Liste der Methoden für integrierte Diktate zu erhalten. Von diesen gehe ich davon aus, dass ich eine Teilmenge von implementieren muss

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

Gibt es einen Unterschied, welche Methoden für Python3 im Gegensatz zu Python2 implementiert werden müssen?

Matt Fenwick
quelle
3
Gute Diskussion hier: stackoverflow.com/q/1061283/641766 . Der Unterschied zwischen Python 2.x und 3.x besteht darin, dass __cmp__dieser entfernt wurde.
Zeekay

Antworten:

88

Ich hätte dies fast als Kommentar zu den anderen Antworten gepostet, aber es ist wirklich eine Antwort an und für sich.

Um Ihre Artikel sortierbar zu machen, müssen sie nur implementiert werden __lt__. Dies ist die einzige Methode, die von der eingebauten Sortierung verwendet wird.

Die anderen Vergleiche oder functools.total_orderingwerden nur benötigt, wenn Sie die Vergleichsoperatoren tatsächlich mit Ihrer Klasse verwenden möchten.

Um Ihre Artikel hashbar zu machen, implementieren Sie __hash__wie von anderen angegeben. Sie sollten auch __eq__auf kompatible Weise implementieren - Elemente, die gleichwertig sind, sollten denselben Hash haben.

agf
quelle
Eine schlechte Implementierung von __lt__könnte dazu führen, dass Python unvorhersehbar sortiert wird. (Zum Beispiel, wenn x .__ lt __ (y) und y .__ lt __ (x))
Matt Fenwick
3
Ich weiß nicht, was "unvorhersehbar" ist. Es ist konsistent, wenn genau dieselbe Eingabe eingegeben wird, aber eine andere Eingabereihenfolge kann dazu führen, dass sich verschiedene Elemente in einer anderen Reihenfolge befinden. Ja, wenn Sie den zum Sortieren verwendeten Vergleich nicht ordnungsgemäß implementieren, sortiert Python nicht ordnungsgemäß. Ich würde eine __key__Funktion empfehlen , die die Instanz in ein Tupel verwandelt, und dann nur __lt__( self.__key__() < other.__key__()) und __hash__( hash(self.__key__())) verwenden.
agf
19

Es gibt keinen Unterschied zwischen Python 2 und 3.

Zur Sortierbarkeit:

Sie sollten Vergleichsmethoden definieren. Dies macht Ihre Artikel sortierbar. Im Allgemeinen sollten Sie nicht bevorzugen __cmp__().

Normalerweise benutze ich den Dekorator functools.total_ordering.

functools.total_ordering (cls) Bei einer Klasse, die eine oder mehrere umfangreiche Vergleichsordnungsmethoden definiert, liefert dieser Klassendekorateur den Rest. Dies vereinfacht den Aufwand für die Angabe aller möglichen umfangreichen Vergleichsoperationen:

Die Klasse muss eine von definieren __lt__(), __le__(), __gt__(), oder __ge__(). Zusätzlich sollte die Klasse eine __eq__()Methode bereitstellen .

Sie sollten darauf achten, dass Ihre Vergleichsmethoden keine Nebenwirkungen haben. (Ändern Sie einen der Werte des Objekts)

Zum Hashing:

Sie sollten die __hash__()Methode implementieren . Ich denke, der beste Weg ist die Rückkehr hash(repr(self)), also wäre Ihr Hash einzigartig.

utdemir
quelle
Ein Beispiel functools.total_orderingaus der Dokumentation finden Sie hier .
Evgeni Sergeev
3

Es gibt verschiedene Möglichkeiten, Ihr Objekt sortierbar zu markieren. Erster Vergleich, definiert durch eine Reihe von Funktionen:

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

Es ist auch möglich, nur eine Funktion zu definieren:

object.__cmp__(self, other)

Und die letzte sollte definiert werden, wenn Sie eine benutzerdefinierte __hash__Funktion definieren möchten . Siehe das Dokument .

Roman Bodnarchuk
quelle
6
In Python 3 wird "[...] die __cmp__()spezielle Methode nicht mehr unterstützt", siehe den entsprechenden Abschnitt hier .
Evgeni Sergeev
-3

Die Implementierungsmethode __lt__(self,other)ist die Antwort, um Ihre Klasse sortierbar zu machen.
Es kann nicht nur für die integrierte Methode verwendet werden sorted(iterable), sondern auch für die Prioritätswarteschlange über das heapqModul.

Außerdem mag ich das Design von Python nicht, so dass viele '__ge__', '__gt__', '__le__', '__lt__', '__ne__'Methoden überhaupt nicht intuitiv sind !
Im Gegensatz dazu gibt Java Interface Comparable<T> (siehe Java-Dokument ) eine negative Ganzzahl, eine Null oder eine positive Ganzzahl zurück, da dieses Objekt kleiner, gleich oder größer als das angegebene Objekt ist, was direkt und freundlich ist !

Yichudu
quelle
9
Der größte Teil Ihrer Antwort deckt Ihre Meinung ab (die nicht Teil der Antwort sein sollte).
Skyking
@skyking ... obwohl ich mit den Meinungen in dieser speziellen Antwort nicht einverstanden bin, glaube ich, dass die Meinung (mit recherchierten und nützlichen Daten, die für die Frage relevant sind) sehr wertvoll ist. Der Fehler in dieser Stellungnahme stützt die Stellungnahme nicht mit relevanten Daten. Das Verstehen von Präferenzen hilft Menschen, Entscheidungen zu treffen, und ist daher sehr nützlich. Hier ist die Antwort schlecht formuliert, da es keinen nützlichen und umsetzbaren Python-Code gibt, der eine pythonische Art des Codierens basierend auf den Präferenzen des Autors veranschaulicht.
Andrew