这是一个很好的例子,说明为什么不应直接使用 __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
有关哪些值被认为是真值和假值的更多信息,请参阅Truth Value Testing 和this answer 上的文档部分。这里值得注意的是,NotImplemented 是真实的,但如果该类定义了一个分别返回 False 或 0 的 __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 是一个特殊的对象,任何时候内存中只存在一个版本。 IOW,它是 NoneType 类的唯一单例(但同一个对象可能有任意数量的引用)。 PEP8 guidelines 明确表示:
与None 等单例的比较应始终使用is 或
is not,绝不是相等运算符。
总之,对于像None 这样的单例,使用is 进行参考检查更合适,尽管== 和is 都可以正常工作。