python working 為什麼`if None.__ eq__(“a”)`似乎評估為True(但不完全)?




string comparison (4)

這是為什麼不應該直接使用 __dunder__ 方法的一個很好的例子,因為它們通常不適合替代它們的等效運算符; 你應該使用 == 運算符來進行相等比較,或者在這種特殊情況下,當檢查 None ,使用 is (跳到答案的底部以獲取更多信息)。

你做完了

None.__eq__('a')
# NotImplemented

由於被比較的類型不同,因此返回 NotImplemented 。 考慮另一個例子,其中以這種方式比較具有不同類型的兩個對象,例如 1'a' 。 執行 (1).__eq__('a') 也不正確,並將返回 NotImplemented 。 將這兩個值進行比較的正確方法是

1 == 'a'
# False

這裡發生的是

  1. 首先,嘗試 (1).__eq__('a') ,返回 NotImplemented 。 這表示不支持該操作,因此
  2. 調用 'a'.__eq__(1) ,它也返回相同的 NotImplemented 。 所以,
  3. 對像被視為不相同,並返回 False

這是一個很好的小MCVE使用一些自定義類來說明這是如何發生的:

class A:
    def __eq__(self, other):
        print('A.__eq__')
        return NotImplemented

class B:
    def __eq__(self, other):
        print('B.__eq__')
        return NotImplemented

class C:
    def __eq__(self, other):
        print('C.__eq__')
        return True

a = A()
b = B()
c = C()

print(a == b)
# A.__eq__
# B.__eq__
# False

print(a == c)
# A.__eq__
# C.__eq__
# True

print(c == a)
# C.__eq__
# True

當然,這並不能解釋 為什麼 操作返回true。 這是因為 NotImplemented 實際上是一個真正的價值:

bool(None.__eq__("a"))
# True

與...一樣,

bool(NotImplemented)
# True

有關哪些值被視為真實和虛假的更多信息,請參閱 真值測試 的文檔部分以及 此答案 。 值得注意的是 NotImplemented 是真實的,但如果類定義了一個分別返回 False0__bool____len__ 方法,那將會是一個不同的故事。

如果你想要與 == 運算符的功能等價,請使用 operator.eq

import operator
operator.eq(1, 'a')
# False

但是,如前所述,對於 此特定方案 ,您要檢查 None ,請使用:

var = 'a'
var is None
# False

var2 = None
var2 is None
# True

功能等同於使用 operator.is_

operator.is_(var2, None)
# True

None 是一個特殊對象,在任何時間點內存中只有1個版本。 IOW,它是 NoneType 類的唯一單例(但同一個對象可能有任意數量的引用)。 PEP8指南 明確指出:

None 這樣的單例的比較應該總是使用 is 或者 is not ,而不是相等的運算符。

總之,對於像 None 這樣的單例,使用 is 的引用檢查更合適,儘管兩者都是 == 並且可以正常工作。

如果在Python 3.7中執行以下語句,它將(從我的測試中)打印 b

if None.__eq__("a"):
    print("b")

但是, None.__eq__("a") 計算結果為 NotImplemented

當然, "a".__eq__("a") 計算結果為 True ,而 "b".__eq__("a") 計算結果為 False

我最初在測試函數的返回值時發現了這一點,但在第二種情況下沒有返回任何內容 - 因此,函數返回 None

這裡發生了什麼?


你看到的結果是由那個事實引起的

None.__eq__("a") # evaluates to NotImplemented

評估為 NotImplementedNotImplemented 的真值記錄為 True

https://docs.python.org/3/library/constants.html

二進制特殊方法應返回的特殊值(例如 __eq__()__lt__() __add__()__rsub__() __add__()__rsub__() 等),表示該操作未針對其他類型實現; 可以由就地二進制特殊方法返回(例如 __imul__()__iand__() 等)以達到相同的目的。 它的真實價值是真實的。

如果手動調用 __eq()__ 方法而不是僅使用 == ,則需要準備好處理它可能返回 NotImplemented 並且其真值為true的可能性。


正如您已經想到的那樣 None.__eq__("a") 評估為 NotImplemented 然而如果您嘗試類似的東西

if NotImplemented:
    print("Yes")
else:
    print("No")

結果是

這意味著 NotImplemented true 值是 true

因此,問題的結果是顯而易見的:

None.__eq__(something) 產生 NotImplemented

並且 bool(NotImplemented) 計算結果為True

因此, if None.__eq__("a") 始終為True


為什麼?

它返回 NotImplemented ,是的:

>>> None.__eq__('a')
NotImplemented
>>> 

但如果你看看這個:

>>> bool(NotImplemented)
True
>>> 

NotImplemented 實際上是一個真正的值,所以這就是它返回 b 的原因,任何 True 都會通過,任何 False 都不會。

怎麼解決?

您必須檢查它是否為 True ,因此更加可疑,如您所見:

>>> NotImplemented == True
False
>>> 

所以你會這樣做:

>>> if None.__eq__('a') == True:
    print('b')


>>> 

如你所見,它不會返回任何東西。





equivalence