2010-02-17 17 views
35

Die Python-Dokumente geben eindeutig an, dass x==yx.__eq__(y) aufruft. Es scheint jedoch, dass unter vielen Umständen das Gegenteil der Fall ist. Wo ist dokumentiert, wann oder warum das passiert und wie kann ich sicher herausfinden, ob die Methoden oder __eq__ meines Objekts aufgerufen werden?Warum/Wenn in Python `x == y` ruft `y .__ eq __ (x)`?

Edit: Nur um zu klären, ich weiß, dass __eq__ in preferecne zu __cmp__ genannt wird, aber ich bin mir nicht klar, warum y.__eq__(x) in Vorzug x.__eq__(y) genannt wird, wenn es sich dabei, was der docs Zustand passieren wird.

>>> class TestCmp(object): 
...  def __cmp__(self, other): 
...   print "__cmp__ got called" 
...   return 0 
... 
>>> class TestEq(object): 
...  def __eq__(self, other): 
...   print "__eq__ got called" 
...   return True 
... 
>>> tc = TestCmp() 
>>> te = TestEq() 
>>> 
>>> 1 == tc 
__cmp__ got called 
True 
>>> tc == 1 
__cmp__ got called 
True 
>>> 
>>> 1 == te 
__eq__ got called 
True 
>>> te == 1 
__eq__ got called 
True 
>>> 
>>> class TestStrCmp(str): 
...  def __new__(cls, value): 
...   return str.__new__(cls, value) 
...  
...  def __cmp__(self, other): 
...   print "__cmp__ got called" 
...   return 0 
... 
>>> class TestStrEq(str): 
...  def __new__(cls, value): 
...   return str.__new__(cls, value) 
...  
...  def __eq__(self, other): 
...   print "__eq__ got called" 
...   return True 
... 
>>> tsc = TestStrCmp("a") 
>>> tse = TestStrEq("a") 
>>> 
>>> "b" == tsc 
False 
>>> tsc == "b" 
False 
>>> 
>>> "b" == tse 
__eq__ got called 
True 
>>> tse == "b" 
__eq__ got called 
True 

Edit: Von Mark Dickinson Antwort und kommentieren es, dass erscheinen würde:

  1. Rich-Vergleich überschreibt __cmp__
  2. __eq__ ist es selbst ist __rop__ es ist __op__ (und ähnlich für __lt__, __ge__, etc)
  3. Wenn das linke Objekt eine eingebaute oder neue Stilklasse ist und das Recht eine Unterklasse davon ist, ist das richtige Objekt __rop__ versucht wird, bevor das linke Objekt der __op__

Dieses Verhalten in den TestStrCmp Beispiele erläutert. TestStrCmp ist eine Unterklasse von str aber nicht implementiert seinen eigenen __eq__ so die __eq__ von str Vorrang in beiden Fällen nehmen (dh tsc == "b" nennt b.__eq__(tsc) als __rop__ wegen Regel 1).

In den TestStrEq Beispielen tse.__eq__ wird in beiden Fällen genannt, weil TestStrEq eine Unterklasse von str ist und so wird es bevorzugt genannt.

In den TestEq Beispielen TestEq implementiert __eq__ und int nicht so __eq__ wird beide Male (Regel 1) genannt.

Aber ich verstehe immer noch nicht das erste Beispiel mit TestCmp. tc ist keine Unterklasse auf int so sollte AFAICT 1.__cmp__(tc) aufgerufen werden, ist es aber nicht.

Antwort

29

Sie sind eine wichtige Ausnahme von dem üblichen Verhalten fehlen: wenn die rechte Operand ist eine Instanz einer Unterklasse der Klasse des linken Operanden, die spezielle Methode für den rechten Operanden heißt zuerst.

finden Sie in der Dokumentation unter:

http://docs.python.org/reference/datamodel.html#coercion-rules

und insbesondere die folgenden zwei Absätze:

Für Objekte x und y, erste x.__op__(y) versucht. Ist dies nicht implementiert oder gibt NotImplemented zurück, wird y.__rop__(x) ausprobiert. Wenn dies auch nicht implementiert ist oder NotImplemented zurückgibt, wird eine TypeError-Ausnahme ausgelöst. Aber sehen die folgende Ausnahme:

Ausnahme zum vorherigen Element: Wenn der linke Operand eine Instanz eines eingebauten Typ oder einen neuen Stil Klasse, und die rechte Operand eine Instanz eines richtige Unterklasse dieser Art oder Klasse und überschreibt die __rop__() Methode der Basis, der rechten Operanden __rop__() Methode vor dem linken Operanden versucht wird __op__() Methode.

+0

@Daniel Pryden: Danke für die Formatierung behoben! Ich werde versuchen, mich beim nächsten Mal an blockquote zu erinnern. –

+0

Schön, aber ich dachte, (aber bin mir nicht sicher), dass alle "__rop__" Methoden veraltet waren. Auch ich benutze keine von ihnen. – Singletoned

+4

Vereinbar, dass Sie keine '__rop__' Methoden verwenden. Die Vergleichsmethoden sind in dieser Hinsicht besonders: "__eq__" ist eine eigene Umkehrung, also lesen Sie "__eq__" für "__op__" und "__rop__". (Ähnlich ist "__ne__" eine eigene Umkehrung, "__le__" ist die Umkehrung von "__ge__", etc.) Andere haben vorher (richtigerweise IMO) kommentiert, dass die Dokumentation hier etwas Arbeit gebrauchen könnte. Ich bin fast sicher, dass die '__rop__' Methoden nicht veraltet sind! –

1

Ist dies nicht in der Language Reference dokumentiert? Nur von einem schnellen Blick dort, es sieht aus wie __cmp__ wird ignoriert, wenn __eq__, __lt__, etc definiert sind. Ich verstehe, dass der Fall, wo __eq__ für eine Elternklasse definiert ist. str.__eq__ ist bereits definiert, so dass __cmp__ auf seinen Unterklassen ignoriert werden. object.__eq__ usw. sind nicht definiert, so __cmp__ auf seine Unterklassen wird geehrt werden.

Als Antwort auf die Frage zu klären:

Ich weiß, dass __eq__ in preferecne zu __cmp__ genannt wird, aber ich bin nicht klar, warum y.__eq__(x) in bevorzugt x.__eq__(y) genannt wird, wenn die letztere ist, was der Docs-Zustand passieren wird.

Docs sagen x.__eq__(y) zuerst genannt wird, aber es hat die Möglichkeit, zurückzukehren NotImplemented in diesem Fall y.__eq__(x) genannt wird. Ich bin mir nicht sicher, warum du dir sicher bist, dass etwas anderes hier vorgeht.

In welchem ​​Fall sind Sie besonders verwirrt? Ich verstehe Sie nur verwirrt über die "b" == tsc und tsc == "b" Fälle, richtig? In jedem Fall wird str.__eq__(onething, otherthing) aufgerufen. Da Sie nicht die __eq__-Methode in TestStrCmp überschreiben, schließlich verlassen Sie sich nur auf die Basis-String-Methode und es heißt, die Objekte sind nicht gleich.

Ohne die Details der Implementierung von str.__eq__ zu wissen, ich weiß nicht, ob ("b").__eq__(tsc)NotImplemented zurück und gibt tsc eine Chance, den Gleichheitstest zu behandeln. Aber selbst wenn dies der Fall ist, wie Sie TestStrCmp definiert haben, erhalten Sie immer noch ein falsches Ergebnis.

So ist es nicht klar, was Sie hier sehen, das ist unerwartet.

Vielleicht, was passiert ist, dass Python __eq__ zu __cmp__ ist lieber, wenn es auf entweder der Objekte, die verglichen werden definiert ist, während Sie __cmp__ auf dem äußersten linken Objekt erwartet hatten Vorrang vor __eq__ auf der rechten Objekt zu haben. Ist es das?

+0

Nachdem ich mit diesem etwas mehr gespielt habe, denke ich, dass Sie Recht haben, dass '__eq__' in beiden Fällen für jedes Objekt bevorzugt wird. – Singletoned

1

Wie ich weiß, ist __eq__() eine sogenannte "Rich Comparison" -Methode, und wird für Vergleichsoperatoren bevorzugt __cmp__() unten genannt. __cmp__() wird aufgerufen, wenn "Rich-Vergleich" nicht definiert ist.

So in A == B:
Wenn __eq__() in A definiert ist, wird es
Else __cmp__() genannt wird

__eq__() in 'str' definiert aufgerufen werden, damit Ihre __cmp__() Funktion wurde nicht genannt.

Die gleiche Regel ist für __ne__(), __gt__(), __ge__(), __lt__() und __le__() "Rich-Vergleich" Methoden.

6

Eigentlich im docs, heißt es:

[__cmp__ ist c] Ursprungserzeugnisse durch Vergleichsoperationen, wenn reich Vergleich (siehe oben) nicht definiert ist.

__eq__ ist ein reiches Vergleichsverfahren und im Fall von TestCmp, nicht definiert ist, damit die Berufung von __cmp__

+0

Aber 'str .__ eq__' ist definiert, daher ist vermutlich' TestStrCmp .__ eq__' definiert (vererbt). – dubiousjim

+0

Sie haben Recht. Ich habe die entsprechende Bearbeitung vorgenommen ... danke – Dancrumb

+1

Sie haben Recht, dass '__eq__'' __cmp__' überschreibt, aber das war nicht das überraschende Verhalten. Die Überraschung war, dass es das richtige Objekt und nicht das linke Objekt anruft. (Ich habe die Frage aktualisiert, um dies ein wenig zu verdeutlichen). – Singletoned