此算法的 Python 3 更改/更新
在 Python 中如何__eq__
处理以及按什么顺序处理?
a == b
通常理解,但并非总是如此,a == b
调用a.__eq__(b)
, 或type(a).__eq__(a, b)
。
明确地,评估的顺序是:
- 如果
b
' 的类型是 ' 类型的严格子类(不是同一类型)a
并且具有__eq__
,则调用它并在实现比较时返回值,
- 否则,如果
a
有__eq__
,则调用它并在实现比较时返回它,
- 否则,看看我们是否没有调用 b
__eq__
并且它有它,然后如果实现了比较,则调用并返回它,
- 否则,最后,进行身份比较,与 相同的比较
is
。
如果方法返回,我们知道是否没有实现比较NotImplemented
。
(在 Python 2 中,__cmp__
曾寻找过一种方法,但在 Python 3 中已弃用并删除了该方法。)
让我们通过让 B 子类 A 来为自己测试第一个检查的行为,这表明接受的答案在这个计数上是错误的:
class A:
value = 3
def __eq__(self, other):
print('A __eq__ called')
return self.value == other.value
class B(A):
value = 4
def __eq__(self, other):
print('B __eq__ called')
return self.value == other.value
a, b = A(), B()
a == b
仅B __eq__ called
在返回之前打印False
。
请注意,我还更正了问题中的一个小错误,即与 whereself.value
进行比较other
而不是other.value
- 在此比较中,我们得到两个对象 ( self
and other
),通常是相同类型的,因为我们在这里没有进行类型检查(但它们可以是不同的类型),我们需要知道它们是否相等。我们衡量它们是否相等的方法是检查value
属性,这必须在两个对象上完成。
我们怎么知道这个完整的算法?
此处的其他答案似乎不完整且已过时,因此我将更新信息并向您展示如何自己查找。
这是在 C 级别处理的。
我们需要在这里查看两段不同的代码——__eq__
类对象的默认值object
,以及查找和调用__eq__
方法的代码,无论它使用默认值__eq__
还是自定义方法。
默认__eq__
__eq__
在相关的 C api 文档中查找向我们展示了__eq__
由tp_richcompare
- 在"object"
类型定义中cpython/Objects/typeobject.c
定义的object_richcompare
for 处理的case Py_EQ:
。
case Py_EQ:
/* Return NotImplemented instead of False, so if two
objects are compared, both get a chance at the
comparison. See issue #1393. */
res = (self == other) ? Py_True : Py_NotImplemented;
Py_INCREF(res);
break;
所以在这里,如果self == other
我们返回True
,否则我们返回NotImplemented
对象。这是任何未实现自己的__eq__
方法的对象子类的默认行为。
如何__eq__
被调用
然后我们找到 C API 文档PyObject_RichCompare函数,它调用do_richcompare
.
然后我们看到为C 定义tp_richcompare
创建的函数是由 调用的,所以让我们更仔细地看一下。"object"
do_richcompare
此函数的第一个检查是比较对象的条件:
- 不是同一类型,但
- 第二个类型是第一个类型的子类,并且
- 第二种类型有一个
__eq__
方法,
然后用交换的参数调用另一个方法,如果实现则返回值。如果该方法没有实现,我们继续......
if (!Py_IS_TYPE(v, Py_TYPE(w)) &&
PyType_IsSubtype(Py_TYPE(w), Py_TYPE(v)) &&
(f = Py_TYPE(w)->tp_richcompare) != NULL) {
checked_reverse_op = 1;
res = (*f)(w, v, _Py_SwappedOp[op]);
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
接下来我们看看是否可以__eq__
从第一种类型中查找方法并调用它。只要结果不是NotImplemented,也就是实现了,我们就返回。
if ((f = Py_TYPE(v)->tp_richcompare) != NULL) {
res = (*f)(v, w, op);
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
否则,如果我们没有尝试其他类型的方法并且它在那里,我们然后尝试它,如果实现了比较,我们返回它。
if (!checked_reverse_op && (f = Py_TYPE(w)->tp_richcompare) != NULL) {
res = (*f)(w, v, _Py_SwappedOp[op]);
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
}
最后,我们得到一个回退,以防它没有为任何一个类型实现。
回退检查对象的身份,即它是否是内存中同一位置的同一对象 - 这与检查相同self is other
:
/* If neither object implements it, provide a sensible default
for == and !=, but raise an exception for ordering. */
switch (op) {
case Py_EQ:
res = (v == w) ? Py_True : Py_False;
break;
结论
在比较中,我们首先尊重比较的子类实现。
然后我们尝试与第一个对象的实现进行比较,然后与第二个对象的实现进行比较,如果它没有被调用。
最后,我们使用身份测试来比较相等性。