sto*_*tic 19 python python-2.7
在python 2.7.6中,假设我有一个定义__eq__的类及其子类:
>>> class A(object):
... def __eq__(self,other):
... print self.__class__,other.__class__
... return True
...
>>> class B(A):
... pass
...
Run Code Online (Sandbox Code Playgroud)
现在我创建每个类的对象,并想要比较它们:
>>> a = A()
>>> b = B()
>>> a==b
Run Code Online (Sandbox Code Playgroud)
结果我得到:
<class '__main__.B'> <class '__main__.A'>
Run Code Online (Sandbox Code Playgroud)
这表明解释器正在调用b.__eq__(a),而不是a.__eq__(b)预期的.
该文档的状态(强调):
对于对象
x和y,首先x.__op__(y)是尝试.如果没有实现或返回NotImplemented,y.__rop__(x)则尝试.如果这也没有实现或返回NotImplemented,TypeError则引发异常.但请参阅以下异常:前一项的异常:如果左操作数是内置类型或新样式类的实例,并且右操作数是该类型或类的正确子类的实例并覆盖基
__rop__()类的方法,则右操作数的__rop__()方法在左操作数的__op__()方法之前尝试.这样做是为了使子类可以完全覆盖二元运算符.否则,左操作数的
__op__()方法将始终接受右操作数:当期望给定类的实例时,该类的子类的实例始终是可接受的.
由于子类B不会覆盖__eq__运算符,不a.__eq__(b)
应该调用而不是b.__eq__(a)?这是预期的行为,还是一个错误?当我读到它时,它与文档相反:我是在误读文档还是遗漏了其他内容?
一些相关问题:
这个答案引用了我上面引用的文档.在这种情况下,最后一个问题涉及内置类型(1)的对象和新样式类的实例之间的比较.在这里,我特意将父类的实例与子类的实例进行比较,该实例不会覆盖其父类的rop()方法(在这种情况下,__eq__两者都是op()和rop()).
在这种情况下,python实际上会调用b.__eq__(a)而不是a.__eq__(b)第一个,即使类B没有显式覆盖A.
Bre*_*arn 13
看起来子类被认为是"覆盖"超类行为,即使它所做的一切都是继承超类行为.在这种__eq__情况下很难看到,因为__eq__它是自己的反射,但如果使用不同的运算符,例如__lt__和__gt__,这是彼此的反射,你可以更清楚地看到它:
class A(object):
def __gt__(self,other):
print "GT", self.__class__, other.__class__
def __lt__(self,other):
print "LT", self.__class__, other.__class__
class B(A):
pass
Run Code Online (Sandbox Code Playgroud)
然后:
>>> A() > B()
LT <class '__main__.B'> <class '__main__.A'>
Run Code Online (Sandbox Code Playgroud)
注意A.__gt__没有被调用; 相反,B.__lt__被称为.
在Python 3中的文档是说明性的,因为它在规定在技术上更准确的(强调)不同的单词的规则:
如果右操作数的类型是左操作数类型的子类,并且该子类提供了操作的反射方法,则此方法将在左操作数的非反射方法之前调用.此行为允许子类覆盖其祖先的操作.
子类确实"提供"了反射方法,它只是通过继承提供它.如果你实际上删除了子类中反射的方法行为(通过返回NotImplemented),则正确调用超类方法(在子类1之后):
class A(object):
def __gt__(self,other):
print "GT", self.__class__, other.__class__
def __lt__(self,other):
print "LT", self.__class__, other.__class__
class B(A):
def __lt__(self, other):
print "LT", self.__class__, other.__class__
return NotImplemented
>>> A() > B()
LT <class '__main__.B'> <class '__main__.A'>
GT <class '__main__.A'> <class '__main__.B'>
Run Code Online (Sandbox Code Playgroud)
所以基本上这似乎是一个文档错误.应该说,无论子类是否显式覆盖超类实现,总是先尝试子类反射方法(对于比较运算符).(正如Mark Dickinson在评论中指出的那样,它只对比较运算符起作用,而不是像__add__/ 这样的数学运算符对__radd__.)
实际上,这不太重要,因为你唯一一次注意到子类不会覆盖超类的时候.但是在这种情况下,子类行为的定义与超类的相同,所以调用哪一个并不重要(除非你做了一些危险的事情,比如在比较方法中改变对象,在这种情况下无论如何你应该保持警惕).
这是实现所描述逻辑的代码:
/* Macro to get the tp_richcompare field of a type if defined */
#define RICHCOMPARE(t) (PyType_HasFeature((t), Py_TPFLAGS_HAVE_RICHCOMPARE) \
? (t)->tp_richcompare : NULL)
...
static PyObject *
try_rich_compare(PyObject *v, PyObject *w, int op)
{
richcmpfunc f;
PyObject *res;
if (v->ob_type != w->ob_type &&
PyType_IsSubtype(w->ob_type, v->ob_type) &&
(f = RICHCOMPARE(w->ob_type)) != NULL) {
res = (*f)(w, v, _Py_SwappedOp[op]); // We're executing this
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
}
if ((f = RICHCOMPARE(v->ob_type)) != NULL) {
res = (*f)(v, w, op); // Instead of this.
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
}
if ((f = RICHCOMPARE(w->ob_type)) != NULL) {
return (*f)(w, v, _Py_SwappedOp[op]);
}
res = Py_NotImplemented;
Py_INCREF(res);
return res;
}
Run Code Online (Sandbox Code Playgroud)
/* Perform a rich comparison, raising TypeError when the requested comparison
operator is not supported. */
static PyObject *
do_richcompare(PyObject *v, PyObject *w, int op)
{
richcmpfunc f;
PyObject *res;
int checked_reverse_op = 0;
if (v->ob_type != w->ob_type &&
PyType_IsSubtype(w->ob_type, v->ob_type) &&
(f = w->ob_type->tp_richcompare) != NULL) {
checked_reverse_op = 1;
res = (*f)(w, v, _Py_SwappedOp[op]); // We're executing this
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
}
if ((f = v->ob_type->tp_richcompare) != NULL) {
res = (*f)(v, w, op); // Instead of this.
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
}
if (!checked_reverse_op && (f = w->ob_type->tp_richcompare) != NULL) {
res = (*f)(w, v, _Py_SwappedOp[op]);
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
}
Run Code Online (Sandbox Code Playgroud)
这两个版本是相似的,只是Python 2.7版本使用RICHCOMPARE宏来检查PyType_HasFeature((t), Py_TPFLAGS_HAVE_RICHCOMPARE而不是ob_type->tp_richcompare != NULL.
在这两个版本中,第一个if块的评估结果为true.根据文档中的描述,人们可能期望是假的特定部分是:( f = w->ob_type->tp_richcompare != NULL对于Py3)/ PyType_HasFeature((t), Py_TPFLAGS_HAVE_RICHCOMPARE.但是,文档说这tp_richcompare是由子类继承的:
richcmpfunc PyTypeObject.tp_richcompare
指向丰富比较函数的可选指针......
该字段由子类型以及tp_compare和tp_hash继承...
随着2.x版,PyType_HasFeature((t), Py_TPFLAGS_HAVE_RICHCOMPARE也将评估为真,因为Py_TPFLAGS_HAVE_RICHCOMPARE 如果标志为true tp_richcompare,tp_clear和tp_traverse是真实的,所有这些都继承自父.
因此,即使B不提供自己的丰富比较方法,它仍然返回非NULL值,因为它的父类提供它.正如其他人所说,这似乎是一个文档错误; 子类实际上不需要覆盖__eq__父类的方法,它只需要提供一个,甚至通过继承.
| 归档时间: |
|
| 查看次数: |
1535 次 |
| 最近记录: |