2010-02-17 137 views
35

Python文檔明確指出x==y調用x.__eq__(y)。然而,在許多情況下,情況正好相反。在哪裏記錄何時或爲何發生這種情況,以及如何確定我的對象的__cmp____eq__方法是否會被調用。爲什麼/在Python中`x == y`調用`y .__ eq __(x)`?

編輯:只是爲了澄清,我知道__eq__被稱爲preferecne到__cmp__,但我不明白爲什麼y.__eq__(x)被稱爲優先於x.__eq__(y),當後者的文檔狀態會發生什麼。

>>> 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 

編輯:馬克狄金森的回答和評論它會出現:

  1. 豐富的比較覆蓋__cmp__
  2. __eq__是它自己的__rop__到它的__op__(和__lt____ge__等類似)
  3. 如果左對象是內置類或新樣式類,右邊是它的子類,則右對象的__rop__被嘗試過左對象的__op__

這說明在TestStrCmp實例的行爲。 TestStrCmpstr一個子類,但沒有實現自己的__eq__所以str__eq__優先在兩種情況下(即tsc == "b"調用b.__eq__(tsc)__rop__,因爲規則1)。

TestStrEq示例中,在兩個實例中調用tse.__eq__,因爲TestStrEqstr的子類,因此它被優先調用。

TestEq例子,TestEq實現__eq__int不那麼__eq__被調用兩次(第1條)。

但我仍然不明白TestCmp的第一個例子。 tc不是int的子類,所以應該調用AFAICT 1.__cmp__(tc),但不是。

回答

29

你缺少的一個主要例外通常的行爲,因此調用:當右手操作數是左側操作數類的子類的一個實例,右側操作數的特殊方法首先被調用。

見文檔:

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

並且特別地,以下兩段:

對於對象xy,第一 x.__op__(y)試圖。如果這不是 實施或返回 NotImplemented,y.__rop__(x)是 嘗試。如果這還沒有實現 或返回NotImplemented,則會引發 TypeError異常。但見 以下異常:

異常前述條款:如果 左操作數是內置型或新式類, 和正確的操作數 的一個實例的實例 該類型或 類的適當的子類,並覆蓋基的 __rop__()的方法,所述右 操作數的__rop__()方法左操作數的__op__() 方法嘗試過 。

+0

@Daniel Pryden:感謝格式修復!我會盡量記住下次blockquote。 –

+0

不錯,但我認爲,(但我不確定),所有'__rop__'方法都被棄用了。另外我沒有使用任何一個。 – Singletoned

+4

同意你沒有使用任何'__rop__'方法。比較方法在這方面是特殊的:'__eq__'是它自己的反轉,所以'__op__'和'__rop__'都要讀'__eq__'。 (同樣,'__ne__'是它自己的反向,'__le__'是'__ge__'的反向,等等。) 其他人之前已經評論過(正確的IMO)文檔可以在這裏使用一些工作。 我幾乎可以肯定'__rop__'方法不會被棄用! –

1

這是不是記錄在Language Reference?只是從那裏快速查看,當__eq__,__lt__等被定義時,它看起來像__cmp__被忽略。我瞭解到,要包括在父類中定義__eq__的情況。 str.__eq__已經定義了,因此__cmp__在它的子類中將被忽略。 object.__eq__等都沒有定義,所以__cmp__就其子類會受到尊重。

在迴應澄清的問題:

我知道__eq__被稱爲 preferecne到__cmp__,但我不 清楚爲什麼y.__eq__(x)被稱爲 優先x.__eq__(y),當 後者是文檔狀態 發生的事情。

文檔說x.__eq__(y)稱爲第一,但它必須返回NotImplemented在這種情況下y.__eq__(x)被稱爲選項。我不確定爲什麼你有信心在這裏發生不同的事情。

哪種情況下你特別困惑?我只是想知道你對"b" == tsctsc == "b"個案感到疑惑,對嗎?無論哪種情況,都會調用str.__eq__(onething, otherthing)。由於您不覆蓋TestStrCmp中的__eq__方法,因此最終您只是依賴基本字符串方法,並且它說對象不相等。

不知道str.__eq__的實現細節,我不知道("b").__eq__(tsc)是否會返回NotImplemented並給予TSC處理相等性測試的機會。但即使它確實如此,你已經定義了TestStrCmp的方式,你仍然會得到一個錯誤的結果。

所以目前還不清楚你在這裏看到的是什麼意外。

可能發生的事情是,Python是寧願__eq____cmp__如果它在對象要麼定義進行比較,而你是最左邊的對象上預計__cmp____eq__右手邊的對象上具有優先權。是嗎?

+0

在玩過這一點之後,我認爲你是對的,在這些情況下,對任何一個對象都優先考慮__eq__。 – Singletoned

1

據我所知,__eq__()是一種所謂的「富比較法」,比較運算符優先於下面的__cmp__()。如果未定義「豐富比較」,則調用__cmp__()

所以在A == B:
如果__eq__()處於定義它會被稱爲
否則__cmp__()將被稱爲

在 'STR' 定義

__eq__()所以不叫你__cmp__()功能。

相同的規則是對於__ne__(), __gt__(), __ge__(), __lt__()__le__()「豐富比較」方法。

6

實際上,在docs,它指出:

[__cmp__爲c]通過比較操作alled如果富比較(見上文)沒有定義。

__eq__是一個豐富的比較方法,並在TestCmp的情況下,不定義的__cmp__

+0

但是'str .__ eq__'被定義,所以推測'TestStrCmp .__ eq__'被定義(繼承)。 – dubiousjim

+0

你是對的。我做了適當的編輯......謝謝 – Dancrumb

+1

你說得對,'__eq__'覆蓋了__cmp__',但這並不是令人驚訝的行爲。令人驚訝的是,它將它稱爲正確的對象而不是左邊的對象。 (我已經更新了這個問題來澄清這一點)。 – Singletoned

相關問題