为什么以及何时像 Python 中的 `==` 这样的文字比较运算符使用自定义类型的魔术方法而不是内置方法?

Why and when do literal comparison operators like `==` in Python use the magic method of a custom type over a builtin?

docs.python.orgpage on the Python "Data Model" states字面量比较运算双方实现运算的魔术方法时,左操作数的方法与右操作数一起使用 作为参数:

x<y calls x.__lt__(y), x<=y calls x.__le__(y), x==y calls x.__eq__(y), x!=y calls x.__ne__(y), x>y calls x.__gt__(y), and x>=y calls x.__ge__(y).

下面的 class 包装了 builtin tuple 并为这些比较运算符之一实现了一个魔术方法来演示这一点:

class eqtest(tuple):
 def __eq__(self, other):
  print('Equivalence!')

在比较运算符的左侧使用此 class 的实例时,它的行为符合预期:

>>> eqtest((1,2,3)) == (1,2,3)
Equivalence!

但是,自定义 class 的比较运算符似乎即使只在右侧使用它的实例也会被调用:

>>> (1,2,3) == eqtest((1,2,3))
Equivalence!

当显式调用左操作数的魔术方法时,结果也明显不同:

>>> (1,2,3).__eq__(eqtest2((1,2,3)))
True

很容易理解为什么这可能是一个深思熟虑的设计选择,尤其是对于子 classes,以便 return 最有可能从稍后定义的类型中得到有用的结果.但是,由于它明显偏离了记录在案的基本行为,因此很难知道它是如何以及为什么以这种方式足够自信地解释和在生产中使用它的。

在什么情况下,Python 语言和 CPython 参考实现会颠倒比较运算符的顺序,即使双方都提供了有效结果,这在何处记录?

comparisons state that tuples don't know how to compare to other types. tuplerichcompare does Py_RETURN_NOTIMPLEMENTED. However, the PyObject richcompare 上的规则检查子类型,例如您继承的 class,并交换比较顺序(应用对称规则)。

这也记录在您链接的页面中:

If the operands are of different types, and right operand’s type is a direct or indirect subclass of the left operand’s type, the reflected method of the right operand has priority, otherwise the left operand’s method has priority. Virtual subclassing is not considered.

这使子classes 能够实现更具体的行为,这些行为适用于以任何一种方式编写的比较。