为什么`if None .__ eq __(“ a”)`似乎评估为True(但不完全)?

时间:2018-12-31 06:03:37

标签: python python-3.x string boolean-expression equivalence

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

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

但是,None.__eq__("a")的值为NotImplemented

自然,"a".__eq__("a")的值为True,而"b".__eq__("a")的值为False

我最初在测试函数的返回值时发现了这一点,但在第二种情况下却未返回任何内容-因此,该函数返回了None

这是怎么回事?

4 个答案:

答案 0 :(得分:172)

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

有关将哪些值视为真实和错误的更多信息,请参见Truth Value Testingthis answer上的“文档”部分。这里值得注意的是NotImplemented是真实的,但是如果类定义了返回__bool__或{{1的__len__False方法,那将是另一回事}}。


如果要使用与0运算符等效的功能,请使用operator.eq

==

但是,如前所述,对于此特定方案,您要在其中检查import operator operator.eq(1, 'a') # False ,请使用None

is

等效的功能是使用operator.is_

var = 'a'
var is None
# False

var2 = None
var2 is None
# True

operator.is_(var2, None) # True 是一个特殊的对象,并且在任何时间点内存中只有1个版本。 IOW,它是None类的唯一单例(但是同一对象可以具有任意数量的引用)。 PEP8 guidelines明确说明了这一点:

  

NoneType之类的单例进行比较时,应始终使用None或   is,永远不要等于运算符。

总而言之,对于is not之类的单例来说,使用None进行参考检查更为合适,尽管is==都可以正常工作。

答案 1 :(得分:33)

您看到的结果是由于以下事实造成的:

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的可能性。

答案 2 :(得分:16)

您已经知道None.__eq__("a")的取值为NotImplemented,但是,如果您尝试类似的操作

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

结果是

  

这意味着NotImplemented true

的真值

因此,问题的结果显而易见:

None.__eq__(something)产生NotImplemented

bool(NotImplemented)的值为True

所以if None.__eq__("a")始终为真

答案 3 :(得分:1)

为什么?

它返回NotImplemented,是的:

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

但是,如果您看这个:

>>> bool(NotImplemented)
True
>>> 

NotImplemented实际上是一个真实的值,所以这就是它返回b的原因,True的任何内容都会通过,False的任何内容都不会通过。

如何解决?

您必须检查它是否为True,因此请更加怀疑,如您所见:

>>> NotImplemented == True
False
>>> 

所以您会这样做:

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


>>> 

如您所见,它不会返回任何内容。