2010-02-17 12 views
35

Dokumentacja Pythona wyraźnie stwierdza, że ​​x==y dzwoni pod numer x.__eq__(y). Wydaje się jednak, że w wielu okolicznościach jest odwrotnie. Gdzie jest udokumentowane, kiedy i dlaczego tak się dzieje, i jak mogę się upewnić, czy zostaną wywołane metody mojego obiektu? __cmp__ lub __eq__.Dlaczego/Kiedy w Pythonie występuje `x == y` wywołanie` y .__ eq __ (x) `?

Edit: Właśnie w celu wyjaśnienia, wiem, że __eq__ nazywa się preferecne do __cmp__, ale nie jestem jasne, dlaczego y.__eq__(x) nazywa zamiast do x.__eq__(y), gdy ten ostatni jest co państwo docs stanie.

>>> 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: Z odpowiedzi Mark Dickinson i komentarz wydaje się, że:

  1. Rich porównanie nadpisuje __cmp__
  2. __eq__ jest to własny __rop__ aby to __op__ (i podobny do __lt__, __ge__, etc)
  3. Jeśli lewy obiekt jest klasą wbudowaną lub stylem nowym, a prawa jest jego podklasą, właściwy obiekt __rop__ jest sądzony przed lewa obiektu __op__

To wyjaśnia zachowanie w przykładach TestStrCmp. TestStrCmp jest podklasą str, ale nie implementuje własnego __eq__, więc __eq__ z str ma pierwszeństwo w obu przypadkach (tj. tsc == "b" dzwoni b.__eq__(tsc) jako __rop__ z powodu reguły 1).

W przykładach TestStrEq w obu przypadkach wywoływana jest nazwa tse.__eq__, ponieważ TestStrEq jest podklasą str i dlatego jest wywoływana w preferencjach.

W przykładach TestEq, TestEq realizuje __eq__ i int nie tak __eq__ jest wywoływana za każdym razem (zasada 1).

Ale nadal nie rozumiem pierwszego przykładu z TestCmp. tc nie jest podklasą na int, więc AFAICT 1.__cmp__(tc) powinien zostać wywołany, ale nim nie jest.

Odpowiedz

29

tracisz kluczową wyjątek od zwykłego zachowania: kiedy prawostronnego operand jest instancją podklasy klasy operandu po lewej stronie, specjalna metoda dla operandu po prawej stronie jest wywoływana jako pierwsza.

znaleźć w dokumentacji na stronie:

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

aw szczególności następujące dwa akapity:

dla obiektów x i y, pierwsze x.__op__(y) jest sądzony. Jeśli nie jest to zaimplementowane lub zwrócono NotImplemented, y.__rop__(x) jest próbowane. Jeśli to również nie zostanie zaimplementowane lub zwróci NotImplemented, zgłoszony zostanie wyjątek TypeError z . Ale zobaczyć następujący wyjątek:

wyjątek od poprzedniego elementu: jeśli lewy operand jest instancją wbudowanego typu lub klasy nowy styl, i prawy operand jest instancją tematyce Prawidłowa podklasą tego typu lub klasy i nadpisuje __rop__() metodę stacji bazowej, __rop__() metoda prawo operand jest wypróbowany przed __op__() metody lewy argument za.

+0

@ Daniel Pryden: Dzięki za poprawki formatowania! Postaram się zapamiętać cytat następnego razu. –

+0

Niezły, ale myślałem (ale nie jestem pewien), że wszystkie metody "__rop__" były przestarzałe. Również nie używam żadnego z nich. – Singletoned

+4

Uzgodniono, że nie używasz żadnej metody "__rop__". Metody porównania są szczególne pod tym względem: '__eq__' jest własnym odwróceniem, więc przeczytaj' __eq__' zarówno dla '__op__' i' __rop__'. (Podobnie, '__ne__' jest własnym odwróceniem,' __le__' jest odwrotnością '__ge__', itp.) Inni już wcześniej skomentowali (poprawnie, IMO), że dokumentacja mogłaby wykorzystać trochę pracy tutaj. Jestem prawie pewien, że metody "__rop__" nie są przestarzałe! –

1

Czy nie jest to udokumentowane w Language Reference? Już od szybkiego spojrzenia wygląda na to, że __cmp__ jest ignorowane, gdy zdefiniowane są __eq__, . Rozumiem, że należy uwzględnić przypadek, w którym zdefiniowano __eq__ na klasie nadrzędnej. str.__eq__ jest już zdefiniowany, więc __cmp__ na jego podklasach zostanie zignorowane. object.__eq__ itp. Nie są zdefiniowane, dlatego ich podklasy będą honorowane.

W odpowiedzi na pytanie: klarownego

wiem, że __eq__ nazywa się preferecne do __cmp__, ale nie jestem jasne, dlaczego y.__eq__(x) nazywa się preferencją do x.__eq__(y), gdy ostatni jest tym, co stanie się dokumentacja: .

Docs powiedzieć x.__eq__(y) będzie nazwany pierwszy, ale ma możliwość powrotu NotImplemented w takim przypadku y.__eq__(x) jest tzw. Nie jestem pewien, dlaczego jesteś pewien, że dzieje się coś innego.

W której sprawie szczególnie się zastanawiasz? Rozumiem cię tylko po to, by być zaskoczonym co do przypadków "b" == tsc i tsc == "b", zgadza się? W obu przypadkach jest wywoływana str.__eq__(onething, otherthing). Ponieważ nie zastąpiłeś metody __eq__ w TestStrCmp, ostatecznie polegasz na metodzie łańcuchów bazowych i mówisz, że obiekty nie są równe.

Bez znajomości szczegółów implementacji str.__eq__, nie wiem, czy ("b").__eq__(tsc) zwróci NotImplemented i da tsc szansę na obsłużenie testu równości. Ale nawet gdyby tak było, sposób zdefiniowania TestStrCmp nadal będzie skutkować fałszywym wynikiem.

Więc nie jest jasne, co tu widzisz, jest nieoczekiwane.

Być może to, co się dzieje jest to, że Python jest preferowanie __eq__ do __cmp__ jeśli jest zdefiniowana na albo z porównywanych obiektów, podczas gdy ty spodziewali __cmp__ na skrajnej lewej obiektu mieć pierwszeństwo nad __eq__ na obiekcie stroną. Czy to to?

+0

Po zagraniu z tym nieco więcej, myślę, że masz rację, że "__eq__" jest preferowany dla obu obiektów w tych przypadkach. – Singletoned

1

Jak wiem, __eq__() jest tak zwaną metodą "bogatego porównania" i jest wywoływana dla operatorów porównania zamiast do __cmp__() poniżej. __cmp__() jest wywoływane, jeśli "porównanie bogate" nie jest zdefiniowane.

Więc a == B:
Jeśli __eq__() jest zdefiniowana w to nazwać
Else __cmp__() zostanie wywołana

__eq__() zdefiniowane w STR więc funkcja __cmp__() nie został powołany.

Ta sama zasada odnosi się do metod "bogatego porównania" __ne__(), __gt__(), __ge__(), __lt__() i __le__().

6

Faktycznie, w docs, stwierdza:

[__cmp__ to c] alled przez operacje porównywania, jeśli bogaty porównanie (patrz wyżej) nie jest określona.

__eq__ jest bogatym metoda porównania i, w przypadku TestCmp, nie jest zdefiniowane, stąd powołanie __cmp__

+0

Ale 'str .__ eq__' jest zdefiniowany, więc prawdopodobnie zdefiniowano' TestStrCmp .__ eq__' (dziedziczone). – dubiousjim

+0

Masz rację. Dokonałem odpowiedniej edycji ... dzięki – Dancrumb

+1

Masz rację, że '__eq__' nadpisuje' __cmp__', ale nie było to zaskakujące zachowanie. Zaskoczeniem było to, że nazywa to na właściwym obiekcie, a nie na lewym. (Zaktualizowałem pytanie, aby trochę to wyjaśnić). – Singletoned

Powiązane problemy