为什么“ if Nothing.__ eq__ (“ a”)”的值似乎是 True (但不完全是 True) ?

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

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

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

自然地,"a".__eq__("a")计算为 True,而 "b".__eq__("a")计算为 False

我最初是在测试函数的返回值时发现这一点的,但是在第二种情况下没有返回任何值——因此,函数返回 None

这是怎么回事?

6214 次浏览

这是一个很好的例子,说明了为什么不应该直接使用 __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实际上是一个 true 值:

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

就像,

bool(NotImplemented)
# True

有关哪些值被认为是真值和假值的更多信息,请参见关于 真理价值测试这个答案的文档部分。这里值得注意的是,NotImplemented是真实的,但是如果类定义了一个分别返回 False0__bool____len__方法,那么情况就不同了。


如果希望获得与 ==运算符等价的函数,请使用 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是一个特殊的对象,在任何时候内存中只有1个版本。IOW,它是 NoneType类的唯一单例(但是同一个对象可能有任意数量的引用)。PEP8指南明确指出:

与单例(如 None)的比较应该始终使用 isis not,从不使用等式运算符。

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

你所看到的结果是由于

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

评估为 NotImplemented,而 NotImplemented的真值被记录为 True:

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

二进制特殊方法(例如 __eq__()__lt__()__add__()__rsub__()等)应该返回的特殊值,以表明该操作没有针对其他类型实现; 可以用原位二进制特殊方法(例如 __imul__()__iand__()等)为同一目的返回该特殊值。它的真理价值是真实的。

如果您手动调用 __eq()__方法而不仅仅是使用 ==,那么您需要准备好处理它可能返回 NotImplemented并且其真值为 true 的可能性。

正如您已经计算出的 None.__eq__("a")计算为 NotImplemented,但是,如果您尝试这样的东西

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

结果就是

是的

这意味着 NotImplemented的真值 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')




>>>

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