實習小記-python 內置函數__eq__函數引發的探索


亂寫__eq__會發生啥?請看代碼..

 
         
>>> class A: ... def __eq__(self, other):  # 不論發生什么,只要有==做比較,就返回True ... return True ... >>> a = A() >>> b = A() >>> a == b True >>> a != b # 這個地方為什么會返回False? False >>> a > b Traceback (most recent call last): File "<ipython-input-7-1e61210fe837>", line 1, in <module> a > b TypeError: unorderable types: A() > A() >>> x = [1,2,3,b,4,5] >>> x.index(b) # 咦? 0 >>> x.index(2) 1 >>> x.index(4) # 誒? 3 >>> x.index(5) # 艾瑪,啥情況? 3 >>> x.index(6) # 媽呀! 3 >>> x.index(a) # 23333 0
 

當時初次看到這個就被這神奇的現象吸引了,發現新大陸了的感覺。以為發現了BUG。。。問我隔壁的小哥,也說不明白。但可以確定的是,如果我沒有重寫這個__eq__函數的話,下面的操作都是正常的,找不到的就找不到,能找到的就返回正確的index。所以肯定是我__eq__函數寫差了,源碼看不到也不好搜索,於是跑去看文檔。文檔這里寫道:

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

These are the so-called “rich comparison” methods. The correspondence between operator symbols and method names is as follows: x<y calls x.__lt__(y), x<=y calls x.__le__(y), x==y calls x.__eq__(y), x!=y calls x.__ne__(y), x>y calls x.__gt__(y), and x>=y calls x.__ge__(y).

A rich comparison method may return the singleton NotImplemented if it does not implement the operation for a given pair of arguments. By convention, False and True are returned for a successful comparison. However, these methods can return any value, so if the comparison operator is used in a Boolean context (e.g., in the condition of an if statement), Python will call bool() on the value to determine if the result is true or false.

There are no implied relationships among the comparison operators. The truth of x==y does not imply that x!=y is false. Accordingly, when defining __eq__(), one should also define __ne__() so that the operators will behave as expected. See the paragraph on __hash__() for some important notes on creating hashable objects which support custom comparison operations and are usable as dictionary keys.

There are no swapped-argument versions of these methods (to be used when the left argument does not support the operation but the right argument does); rather, __lt__() and __gt__() are each other’s reflection, __le__() and __ge__() are each other’s reflection, and __eq__() and __ne__() are their own reflection.

Arguments to rich comparison methods are never coerced.

To automatically generate ordering operations from a single root operation, see functools.total_ordering().

1.這些比較函數都稱為rich comparison,正常情況下,它們回返回真或假。實際上這些函數都可以返回任意類型的值,如果這些比較被運用在布爾環境中的時候,比方說if 語句的條件中時,python 會調用bool()函數來確認當前的返回值是真還是假。(划橫線的地方不會翻譯=。=)

 

2.這些函數的定義之間並沒有引申的含義,比如說如果a==b是真,並不代表a!=b一定是假。所以我們在定義__eq__函數的時候也應該同時定義__ne__函數以確保我們能夠得到我們想要的結果。如果我們想了解可哈希對象的比較的信息,我們最好去看看__hash__()的文檔。這里就不展開了,具體文章在這里(實習小記-python中可哈希對象是個啥?what is hashable object in python?

 

3.這些函數並沒有可替換參數之說(像是左邊的參數不支持這個函數,而右邊的參數支持。)這里要說的是,__lt__()函數和__gt__()函數他們兩個的取值是一定相反的;__le__()和__ge__()也一定是取相反的;但是__eq__()函數和__ne__()函數是各自獨立定義的。相互之間並沒有關系。這也就是2里面提到的。

 

4.比較這類操作的參數是不會類型轉換的。(如果a,b做比較,a是浮點,b是整數,比較結束后b不會變成浮點類型?)

 

5.如果我們希望能夠在一些情況下自動排序,那么請參考functools.total_ordering()

 

讀到這里我們在回到之前的代碼中看看,到底發生了什么

>>> class A:
...     def __eq__(self, other):  # 不論發生什么,只要有==做比較,就返回True
...         return True
...     
>>> a = A()
>>> b = A()
>>> a == b
True
>>> a != b  # 這個地方為什么會返回False?
False
>>> a > b
Traceback (most recent call last):
  File "<ipython-input-7-1e61210fe837>", line 1, in <module>
    a > b
TypeError: unorderable types: A() > A()

>>> x = [1,2,3,b,4,5]
>>> x.index(b)  # 首先拿x中的1與b做比較,應該是==的比較,由於b.__eq__()無論什么情況下都返回True。所以python以為第一個元素就是b,於是返回index 0
0
>>> x.index(2)
1
>>> x.index(4)  # 這里拿4做比較,x中的元素安順序比較,1,2,3為False,但當比較到b時由於b的坑爹屬性返回True,以為找到了,返回index 3
3
>>> x.index(5)  # 同上
3
>>> x.index(6)  # 同上
3
>>> x.index(a)  # 和第一次相比的原因是一樣的。
0

那么問題來了,如果我們需要一種類,該類的實例做比較的時候比較類中屬性,屬性相等返回True,在列表中查找時也能正確查找,應該如何寫呢?請看下面代碼:

 
           
>>> class A:
...     def __init__(self, x):
...         self.x = x
...     def __eq__(self, other):
...         try:
...             return self.x == other.x
...         except AttributeError:
...             return False
...     def __ne__(self, other):
...         try:
...             return self.x != other.x
...         except AttributeError: # 發現兩者不可比,返回類型錯誤
...             raise TypeError('this two argument is not comparable!')
...         
>>> a = A(1)
>>> b = A(1)
>>> c = A(2)
>>> a == b
True
>>> a != b
False
>>> a == c
False
>>> a != c  # 相同類型做比較,確實不相同
True
>>> a == 1  # 不同類型做比較,返回False
False
>>> a != 1  # 判斷不同類型是否不等,報類型錯誤
Traceback (most recent call last):
  File "<ipython-input-52-30ec16ffc1a6>", line 1, in <module>
    a != 1
  File "<ipython-input-43-70049527ba59>", line 13, in __ne__
    raise TypeError('this two argument is not comparable!')
TypeError: this two argument is not comparable!

>>> list = [1,2,3,b,4,5]
>>> list.index(3)
2
>>> list.index(b)  # 成功找到!
3
>>> list.index(a)  # 實例不同,但是self.x相等,屬於同一個。
3
>>> a in list
True
>>> b in list
True
 
          

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM