Python是如何处理eq的?顺序是什么?
由于Python不提供其比较运算符的左/右版本,它如何决定调用哪个函数Python是如何处理eq的?顺序是什么?,python,comparison,user-defined,Python,Comparison,User Defined,由于Python不提供其比较运算符的左/右版本,它如何决定调用哪个函数 class A(object): def __eq__(self, other): print "A __eq__ called" return self.value == other class B(object): def __eq__(self, other): print "B __eq__ called"
class A(object):
def __eq__(self, other):
print "A __eq__ called"
return self.value == other
class B(object):
def __eq__(self, other):
print "B __eq__ called"
return self.value == other
>>> a = A()
>>> a.value = 3
>>> b = B()
>>> b.value = 4
>>> a == b
"A __eq__ called"
"B __eq__ called"
False
这似乎调用了两个\uuuuu eq\uuu
函数
我正在寻找正式的决策树。表达式调用
a.\uuu eq\uuuu
,因为它存在。其代码包括self.value==other
。由于int不知道如何将自己与B进行比较,Python尝试调用B.\uuu eq\uu
,看看它是否知道如何将自己与int进行比较
如果修改代码以显示要比较的值:
class A(object):
def __eq__(self, other):
print("A __eq__ called: %r == %r ?" % (self, other))
return self.value == other
class B(object):
def __eq__(self, other):
print("B __eq__ called: %r == %r ?" % (self, other))
return self.value == other
a = A()
a.value = 3
b = B()
b.value = 4
a == b
它将打印:
A __eq__ called: <__main__.A object at 0x013BA070> == <__main__.B object at 0x013BA090> ?
B __eq__ called: <__main__.B object at 0x013BA090> == 3 ?
A uuu eq_uuu_uu_uu_uu_uu_uu_u_u_u_u_u_u_u_u_u_u_u_?
B uuu eq uuuu调用:==3?
当Python2.x看到a==b
时,它会尝试以下操作
- 如果
是一个新样式的类,type(b)
是type(b)
的子类,并且type(a)
已经覆盖了type(b)
,那么结果就是\uuueq\uuuu
b.\uueq\uuuuu(a)
- 如果
已覆盖type(a)
(也就是说,\uuuueq\uuuuuuuuu
不是type(a)。\uuuuuuuueq\uuuuuu
),则结果是对象。\uuuuuuueq\uuuuuuuu
a.\uuuuueq\uuuuuuuu(b)
- 如果
已覆盖类型(b)
,则结果为\uuuuueq\uuuu
b.。\uuuuuueq\uuuuiq(a)
- 如果上述情况都不是这样,Python将重复查找
的过程。如果存在,当它返回\uuuucmp\uuuu
零时,对象相等
- 作为最后的回退,Python调用
即对象。
iffTrue
和a
是同一个对象b
NotImplemented
,Python就好像该方法不存在一样
请注意最后一步:如果a
和b
均未重载=
,则a==b
与a是b
相同
在中,我正在为Python 3编写关于这个问题的更新答案 Python是如何处理
\uuuuuuuueq\uuuuuuu
的,顺序是什么?
人们通常理解,但并不总是这样,即a==b
调用a.\uuuueq\uuuuuuuuuuuub
,或类型(a)。\uuuuuueq\uuuuuuuuu(a,b)
明确地说,评估的顺序是:
b
的类型是a
类型的严格子类(不同类型),并且有一个\uuuuuu eq\uuuu
,则调用它并返回值(如果实现了比较)a
具有\uuuuu eq\uuuuu
,则调用它并在执行比较时返回它\uuuu eq\uuuu
,并且它有,然后如果实现了比较,则调用并返回它的比较相同
NotImplemented
,我们就知道比较是否没有实现
(在Python2中,有一个\uu\cmp\uu
方法被查找,但在Python3中被弃用并删除。)
让我们通过让B子类A来测试第一个检查的行为,这表明接受的答案在这方面是错误的:
A类:
值=3
定义(自身、其他):
打印('A _; eq _; called')
返回self.value==other.value
B(A)类:
值=4
定义(自身、其他):
打印('B _; eq _;调用')
返回self.value==other.value
a、 b=a(),b()
a==b
在返回False
之前,仅打印调用的B\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
我们怎么知道这个完整的算法?
这里的其他答案似乎不完整且过时,因此我将更新这些信息,并向您展示如何自己查找这些信息
这是在C级处理的
这里我们需要看两个不同的代码位——类object
的对象的默认\uuuuueq\uuuuu
,以及查找和调用\uuuuueq\uuuuu
方法的代码,不管它是使用默认的\uuuuueq\uuuu
方法还是自定义方法
默认值\uuuu eq\uuuu
查看中的\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
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
对象。这是对象的任何子类的默认行为,该子类不实现自己的\uuuuuueq\uuuuu
方法
如何调用\uuuu eq\uuuu
然后我们找到C API文档,即调用do\u richcompare
的函数
然后我们看到为“object”
C定义创建的tp\u richcompare
函数被do\u richcompare
调用,所以让我们更仔细地看一下
此函数中的第一个检查用于比较对象的条件:
class A(object):
def __eq__(self, other):
print("A __eq__ called: %r == %r ?" % (self, other))
return self.value == other
class B(object):
def __eq__(self, other):
print("B __eq__ called: %r == %r ?" % (self, other))
return self.value == other
a = A()
a.value = 3
b = B()
b.value = 4
a == b
- 不是同一类型,但是
- 第二个类型是第一个类型的子类,并且
- 第二个类型有一个
\uuuu eq\uuu
方法
然后使用交换的参数调用另一个的方法,如果实现,则返回值。如果该方法未实现,我们将继续
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);
接下来,我们看看是否可以从第一个类型中查找\uuuuueq\uuuu
方法并调用它。
只要结果没有实现,也就是说,它已经实现,我们就返回它
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);
}
否则,如果我们没有尝试另一种类型的方法,它就在那里,我们就尝试它,如果实现了比较,我们就返回它
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;
结论