Python 2.x bietet zwei Möglichkeiten, um Vergleichsoperatoren __cmp__
oder die "Rich-Vergleichsoperatoren" wie z __lt__
. Die reichhaltigen Vergleichsüberladungen sollen bevorzugt werden, aber warum ist das so?
Umfangreiche Vergleichsoperatoren sind einfacher zu implementieren, Sie müssen jedoch mehrere davon mit nahezu identischer Logik implementieren. Wenn Sie jedoch die integrierte Reihenfolge cmp
und die Tupelreihenfolge verwenden können , __cmp__
wird dies recht einfach und erfüllt alle Vergleiche:
class A(object):
def __init__(self, name, age, other):
self.name = name
self.age = age
self.other = other
def __cmp__(self, other):
assert isinstance(other, A) # assumption for this example
return cmp((self.name, self.age, self.other),
(other.name, other.age, other.other))
Diese Einfachheit scheint meine Bedürfnisse viel besser zu erfüllen, als alle 6 (!) Der reichhaltigen Vergleiche zu überladen. (Sie können es jedoch auf "nur" 4 reduzieren, wenn Sie sich auf das "getauschte Argument" / reflektiertes Verhalten verlassen, aber dies führt meiner bescheidenen Meinung nach zu einer Nettoerhöhung der Komplikationen.)
Gibt es unvorhergesehene Fallstricke, auf die ich aufmerksam gemacht werden muss, wenn ich nur überlastete __cmp__
?
Ich verstehe das <
, <=
, ==
, etc. können Betreiber für andere Zwecke überlastet werden und kann zurückkehren , jedes Objekt die sie mögen. Ich frage nicht nach den Vorzügen dieses Ansatzes, sondern nur nach Unterschieden, wenn diese Operatoren für Vergleiche in demselben Sinne verwendet werden, wie sie für Zahlen bedeuten.
Update: Wie Christopher betonte , cmp
verschwindet es in 3.x. Gibt es Alternativen, die die Implementierung von Vergleichen so einfach wie oben machen __cmp__
?
quelle
Antworten:
Ja, es ist einfach, alles zu implementieren, z. B.
__lt__
mit einer Mixin-Klasse (oder einer Metaklasse oder einem Klassendekorateur, wenn Ihr Geschmack so ist).Beispielsweise:
Jetzt kann Ihre Klasse nur definieren
__lt__
und das Erben von ComparableMixin multiplizieren (nach allen anderen Basen, die sie benötigt, falls vorhanden). Ein Klassendekorateur wäre ziemlich ähnlich und würde nur ähnliche Funktionen als Attribute der neuen Klasse einfügen, die er dekoriert (das Ergebnis könnte zur Laufzeit mikroskopisch schneller sein, bei gleich geringen Speicherkosten).Wenn Ihre Klasse eine besonders schnelle Möglichkeit zur Implementierung hat (z. B.)
__eq__
und__ne__
diese direkt definieren sollte, damit die Versionen des Mixins nicht verwendet werden (z. B. fürdict
), kann dies__ne__
möglicherweise zur Erleichterung definiert werden das als:aber im obigen Code wollte ich die erfreuliche Symmetrie
<
beibehalten, nur zu verwenden ;-). Wie, warum__cmp__
, gehen musste , da wir taten haben__lt__
und Freunde, halten , warum eine andere, unterschiedliche Art und Weise genau die gleiche Sache um zu tun? Es ist einfach so viel Eigengewicht in jeder Python-Laufzeit (Classic, Jython, IronPython, PyPy, ...). Der Code, der definitiv keine Fehler aufweist, ist der Code, der nicht vorhanden ist - woher Pythons Prinzip, dass es idealerweise einen offensichtlichen Weg geben sollte, eine Aufgabe auszuführen (C hat das gleiche Prinzip im Abschnitt "Spirit of C" von der ISO-Standard übrigens).Dies bedeutet nicht , dass wir unseren Weg gehen zu verbieten , Dinge (zB in der Nähe von -Äquivalenz zwischen Mixins und Klasse Dekorateure für einige Anwendungen), aber es definitiv tut bedeuten , dass wir nicht wie tragen Code in den Compiler und / oder redundant vorhandene Laufzeiten, nur um mehrere äquivalente Ansätze zu unterstützen, um genau dieselbe Aufgabe auszuführen.
Weitere Bearbeitung: Es gibt tatsächlich eine noch bessere Möglichkeit, Vergleiche UND Hashing für viele Klassen bereitzustellen, einschließlich der in der Frage - eine
__key__
Methode, wie ich in meinem Kommentar zur Frage erwähnt habe. Da ich nie dazu gekommen bin, das PEP dafür zu schreiben, müssen Sie es derzeit mit einem Mixin (& c) implementieren, wenn Sie es mögen:Es ist ein sehr häufiger Fall, dass Vergleiche einer Instanz mit anderen Instanzen darauf hinauslaufen, ein Tupel für jede mit einigen Feldern zu vergleichen - und dann sollte Hashing auf genau derselben Basis implementiert werden. Die
__key__
speziellen Methodenadressen, die direkt benötigt werden.quelle
TypeError: Cannot create a consistent method resolution order (MRO) for bases object, ComparableMixin
Wenn ich dies in Python 3 versuche. Den vollständigen Code finden Sie unter gist.github.com/2696496functools.total_ordering
anstatt Ihre eigenen zu erstellenComparableMixim
. Wie in jmagnussons Antwort vorgeschlagen<
zur Implementierung__eq__
in Python 3 ist aus diesem Grund eine ziemlich schlechte IdeeTypeError: unorderable types
.Um diesen Fall zu vereinfachen, gibt es in Python 2.7 + / 3.2 + einen Klassendekorator , functools.total_ordering , mit dem implementiert werden kann, was Alex vorschlägt. Beispiel aus den Dokumenten:
quelle
total_ordering
implementiert aber nicht__ne__
, also aufgepasst!__ne__
. Dies liegt jedoch daran, dass die__ne__
Standardimplementierung delegiert wird__eq__
. Hier gibt es also nichts zu beachten.Dies wird durch PEP 207 - Rich Comparisons abgedeckt
Auch
__cmp__
geht in Python 3.0 weg. (Beachten Sie, dass es nicht auf http://docs.python.org/3.0/reference/datamodel.html vorhanden ist, sondern auf http://docs.python.org/2.7/reference/datamodel.html )quelle
(Bearbeitet am 17.06.17, um Kommentare zu berücksichtigen.)
Ich habe die vergleichbare Mixin-Antwort oben ausprobiert. Ich hatte Probleme mit "None". Hier ist eine modifizierte Version, die Gleichheitsvergleiche mit "Keine" behandelt. (Ich sah keinen Grund, mich mit Ungleichheitsvergleichen mit None als nicht semantisch zu beschäftigen):
quelle
self
könnte das der SingletonNone
von dir seinNoneType
und gleichzeitig dein implementierenComparableMixin
? Und in der Tat ist dieses Rezept schlecht für Python 3.self
wird nie seinNone
, so dass Zweig ganz gehen kann. Nicht benutzentype(other) == type(None)
; einfach benutzenother is None
.None
Testen Sie anstelle eines speziellen Gehäuses , ob der andere Typ eine Instanz des Typs von istself
, und geben Sie denNotImplemented
Singleton zurück, wenn nicht :if not isinstance(other, type(self)): return NotImplemented
. Tun Sie dies für alle Methoden. Python kann dann dem anderen Operanden die Möglichkeit geben, stattdessen eine Antwort zu geben.Inspiriert von Alex Martellis
ComparableMixin
&KeyedMixin
Antworten, habe ich mir das folgende Mixin ausgedacht. Sie können eine einzelne_compare_to()
Methode implementieren , die schlüsselbasierte Vergleiche ähnlich wie verwendetKeyedMixin
, aber Ihrer Klasse ermöglicht, den effizientesten Vergleichsschlüssel basierend auf dem Typ von auszuwählenother
. (Beachten Sie, dass dieses Mixin für Objekte, die auf Gleichheit, aber nicht auf Reihenfolge getestet werden können, nicht viel hilft.)quelle