问题:为什么`if None .__ eq __(“ a”)`似乎评估为True(但不完全)?
如果您在Python 3.7中执行以下语句,它将(根据我的测试)打印b
:
if None.__eq__("a"):
print("b")
但是,None.__eq__("a")
计算为NotImplemented
。
当然,"a".__eq__("a")
计算结果为True
,并"b".__eq__("a")
计算结果为False
。
我最初是在测试函数的返回值时发现此问题的,但是在第二种情况下却未返回任何内容-因此,该函数返回了None
。
这里发生了什么?
回答 0
这是一个很好的例子,说明为什么__dunder__
不应该直接使用这些方法,因为它们通常不是等效操作符的适当替代;您应该使用==
运算符来代替相等性比较,或者在这种特殊情况下,当检查时None
,请使用is
(跳至答案的底部以获取更多信息)。
你做完了
None.__eq__('a')
# NotImplemented
NotImplemented
由于所比较的类型不同,返回的结果不同。考虑另一个示例,其中以这种方式比较了具有不同类型的两个对象,例如1
和'a'
。这样做(1).__eq__('a')
也不正确,并且会返回NotImplemented
。比较这两个值是否相等的正确方法是
1 == 'a'
# False
这里发生的是
- 首先,
(1).__eq__('a')
尝试,然后返回NotImplemented
。这表明不支持该操作,因此 'a'.__eq__(1)
被调用,它也返回相同的NotImplemented
。所以,- 将对象视为不一样,然后
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
是truthy,但它会是一个不同的故事有类中定义一个__bool__
或__len__
方法返回False
或0
分别。
如果要==
使用与运算符等效的功能,请使用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
应始终使用is
或is not
,而不应使用相等运算符。
综上所述,对于单身人士喜欢None
,与基准检查is
是比较合适的,虽然两者==
并is
会工作得很好。
回答 1
您看到的结果是由于以下事实造成的:
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
正如您已经想过的None.__eq__("a")
,NotImplemented
但是如果尝试类似
if NotImplemented:
print("Yes")
else:
print("No")
结果是
是
这意味着 NotImplemented
true
因此,问题的结果显而易见:
None.__eq__(something)
Yield NotImplemented
并bool(NotImplemented)
评估为True
所以if None.__eq__("a")
永远是真的
回答 3
为什么?
它返回一个NotImplemented
,是的:
>>> None.__eq__('a')
NotImplemented
>>>
但是,如果您看一下:
>>> bool(NotImplemented)
True
>>>
NotImplemented
实际上是一个真实的值,所以这就是它返回的原因b
,任何True
会通过的东西,不会通过的东西False
。
怎么解决呢?
您必须检查它是否为True
,因此请更加可疑,如下所示:
>>> NotImplemented == True
False
>>>
所以你会做:
>>> if None.__eq__('a') == True:
print('b')
>>>
如您所见,它不会返回任何内容。