为什么 `if None.__eq__("a")` 的计算结果似乎为 True(但不完全是)?

Why does `if None.__eq__("a")` seem to evaluate to True (but not quite)?

如果在 Python 3.7 中执行以下语句,它将(根据我的测试)打印 b:

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

但是,None.__eq__("a") 的计算结果为 NotImplemented

当然,"a".__eq__("a") 的计算结果为 True,而 "b".__eq__("a") 的计算结果为 False

我最初是在测试函数的 return 值时发现这一点的,但在第二种情况下 return 什么也没发现——因此,函数 returned None.

这是怎么回事?

这是为什么不应直接使用 __dunder__ 方法的一个很好的例子,因为它们通常不是等效运算符的合适替代品;您应该使用 == 运算符来进行相等性比较,或者在这种特殊情况下,在检查 None 时,使用 is (跳至答案底部以获取更多信息)。

你已经完成了

None.__eq__('a')
# NotImplemented

Which returns NotImplemented 因为被比较的类型不同。考虑另一个示例,其中以这种方式比较两个不同类型的对象,例如 1'a'。做(1).__eq__('a')也不对,会returnNotImplemented。比较这两个值是否相等的正确方法是

1 == 'a'
# False

这里发生的是

  1. 首先尝试(1).__eq__('a'),即returns NotImplemented。这表示不支持该操作,所以
  2. 'a'.__eq__(1) 被调用,这也 return 相同 NotImplemented。所以,
  3. 对象被视为不同,False 被 return 编辑。

这是一个不错的小 MCVE,使用一些自定义 classes 来说明这是如何发生的:

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

当然,这并不能解释为什么操作return是正确的。这是因为 NotImplemented 实际上是一个真值:

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

相同
bool(NotImplemented)
# True

有关哪些值被视为真值和假值的更多信息,请参阅 Truth Value Testing, as well as 上的文档部分。这里值得注意的是 NotImplemented 是真实的,但如果 class 定义了 returned 的 __bool____len__ 方法,情况就会不同分别为 False0


如果您想要 == 运算符的等效功能,请使用 operator.eq:

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

但是,如前所述,对于这种特定情况,您正在检查None,请使用is:

var = 'a'
var is None
# False

var2 = None
var2 is None
# True

与此等效的功能是使用 operator.is_:

operator.is_(var2, None)
# True

None是一个特殊的对象,任何时候内存中只存在一个版本。 IOW,它是 NoneType class 的唯一单例(但同一个对象可能有任意数量的引用)。 PEP8 guidelines 明确表示:

Comparisons to singletons like None should always be done with is or is not, never the equality operators.

总之,对于像 None 这样的单例,使用 is 进行参考检查更合适,尽管 ==is 都可以正常工作。

您看到的结果是由于

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

计算为 NotImplementedNotImplemented 的真值记录为 True:

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

Special value which should be returned by the binary special methods (e.g. __eq__(), __lt__(), __add__(), __rsub__(), etc.) to indicate that the operation is not implemented with respect to the other type; may be returned by the in-place binary special methods (e.g. __imul__(), __iand__(), etc.) for the same purpose. Its truth value is true.

如果您手动调用 __eq()__ 方法而不是仅使用 ==,您需要准备好应对可能 return NotImplemented 并且它的真值是真的。

如您所想,None.__eq__("a") 的计算结果为 NotImplemented,但是如果您尝试

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

结果是

yes

这意味着NotImplementedtrue

的真值

所以问题的结果是显而易见的:

None.__eq__(something) 产生 NotImplemented

并且 bool(NotImplemented) 的计算结果为 True

所以 if None.__eq__("a") 总是 True

为什么?

它return是一个NotImplemented,是的:

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

但是如果你看这个:

>>> bool(NotImplemented)
True
>>> 

NotImplemented 实际上是一个真值,所以这就是为什么它 returns b,任何 True 都会通过,任何 False 不会。

如何解决?

你要检查一下是不是True,所以要多点怀疑,如你所见:

>>> NotImplemented == True
False
>>> 

所以你会这样做:

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


>>> 

如你所见,它不会return任何东西。