根据object.__eq__()
文档,==
的默认(即在object
类中)实现如下:
True if x is y else NotImplemented
仍然按照NotImplemented
的文档,我推断NotImplemented
暗示Python运行时将尝试相反的比较,即如果x.__eq__(y)
返回NotImplemented
,则尝试y.__eq__(x)
(在==
操作符的情况下)。
现在,下面的代码在python 3.9中打印False
和True
:
class A:
pass
print(A() == A())
print(bool(NotImplemented))
因此,我的问题是:文档在__eq__
上下文中的何处提到了NotImplemented
的特殊行为?
PS:我在CPython源代码中找到了答案,但我猜这一定/应该在文档中的某个地方。
1条答案
按热度按时间zujrkrfu1#
根据
object.__eq__()
文档,==
的缺省(即在对象类中)实现如下否;这是
__eq__
的默认实现。==
是一个运算符,不能在类中实现。Python的运算符实现是合作的,有硬编码的逻辑使用dunder方法来计算应该发生什么,并且可能福尔斯默认值,这种逻辑是在任何类之外的。
您可以看到内置
len
的另一个示例:一个类可以从它的__len__
方法返回任何它喜欢的东西,原则上你可以直接调用它并得到任何类型的值。然而,这并不能正确地实现协议,当len
没有得到一个非负整数时,它会抱怨。没有任何类包含类型检查和值检查逻辑。它是外部的。仍然按照
NotImplemented
的文档,我推断NotImplemented
暗示Python运行时将尝试相反的比较,即如果x.__eq__(y)
返回NotImplemented
,则尝试y.__eq__(x)
(在==
操作符的情况下)。NotImplemented
只是一个对象,它不是语法,它没有任何特殊行为,在Python中,简单地返回一个值并不会触发特殊行为,除了返回值之外。二元运算符的外部代码将尝试查找匹配的
__op__
,如果__op__
不起作用,则尝试查找匹配的__rop__
。(它是专门为此目的而存在的标记,因为None
是可接受的答案)。通常,如果到目前为止答案仍然是NotImplemented
,则外部代码将是raise NotImplementedError
。作为特殊情况,不提供自身比较的对象(即,
object
中的默认值用于__eq__
或__ne__
)将比较为“不相等”,除非它们相同。(我猜是在类 * 显式 * 将__eq__
或__ne__
直接定义为return NotImplemented
的情况下)这是因为给予这个结果被认为是明智的,而在有一个明智的默认值的情况下让==
一直失败是令人讨厌的。然而,如果没有显式逻辑,这两个对象仍然是不可 * 排序 * 的,因为没有合理的默认值(您可以比较指针值,但它们是任意的,与让您到达该点的Python逻辑没有任何关系;所以这样排序对于编写Python代码实际上没有什么用处。)例如,如果没有提供比较逻辑,
x < y
将引发TypeError
(它这样做 * 即使 *x is y
;在这种情况下,您可以合理地说<=
和>=
应该为真,<
和>
应该为假,但这会使事情变得过于复杂,而且不是很有用。)[观察结果:打印
print(bool(NotImplemented))
打印True
]是的,
NotImplemented
是一个对象,所以默认情况下是true;它不代表一个数值,也不是一个容器,所以它没有理由是假的。然而,这也没有告诉我们任何有用的东西,我们不关心
NotImplemented
的真实性,在Python实现中也没有这样使用它,它只是一个哨兵值。文档在哪里提到了NotImplemented在
__eq__
上下文中的特殊行为?没有,因为它 * 不是
NotImplemented
* 的行为,如上所述。好吧,但还有个潜在的问题:文档在哪里解释了默认情况下
==
操作符的作用?答:因为我们讨论的是一个运算符,而不是一个方法,所以它不在关于dunder方法的章节中,而是在关于表达式的章节6中,具体来说就是6.10.1.值比较:
相等比较的默认行为(
==
和!=
)基于对象的标识。因此,具有相同标识的示例的相等性比较导致相等,对具有不同标识的示例进行比较和相等性比较会导致不相等。这种默认行为的动机是希望所有对象都是自反的(即x is y
意味着x == y
)。