一、问题的发现与提出
在Python类的方法(method)中,要调用父类的某个方法,在Python 2.2以前,通常的写法如代码段1:
代码段1:
class A: def __init__(self): print "enter A" print "leave A"class B(A): def __init__(self): print "enter B" A.__init__(self) print "leave B"
>>> b = B()
enter B enter A leave A leave B
这样做的缺点是,当一个子类的父类发生变化时(如类B的父类由A变为C时),必须遍历整个类定义,把所有的通过非绑定的方法的类名全部替换过来,例如代码段2,
代码段2:
class B(C): # A --> C def __init__(self): print "enter B" C.__init__(self) # A --> C print "leave B"
因此,自Python 2.2开始,Python添加了一个关键字super,来解决这个问题。下面是Python 2.3的官方文档说明:
super(type[, object-or-type])Return the superclass of type. If the second argument is omitted the super object returned is unbound. If the second argument is an object, isinstance(obj, type) must be true. If the second argument is a type, issubclass(type2, type) must be true. super() only works for new-style classes.
A typical use for calling a cooperative superclass method is:
class C(B): def meth(self, arg): super(C, self).meth(arg)
New in version 2.2.
代码段3:
class A(object): # A must be new-style class def __init__(self): print "enter A" print "leave A"class B(C): # A --> C def __init__(self): print "enter B" super(B, self).__init__() print "leave B"
尝试执行上面同样的代码,结果一致,但修改的代码只有一处,把代码的维护量降到最低,是一个不错的用法。因此在我们的开发过程中,super关键字被大量使用,而且一直表现良好。
在我们的印象中,对于super(B, self).__init__()是这样理解的:super(B, self)首先找到B的父类(就是类A),然后把类B的对象self转换为类A的对象(通过某种方式,一直没有考究是什么方式,惭愧),然后“被转换”的类A对象调用自己的__init__函数。考虑到super中只有指明子类的机制,因此,在多继承的类定义中,通常我们保留使用类似代码段1的方法。
有一天某同事设计了一个相对复杂的类体系结构(我们先不要管这个类体系设计得是否合理,仅把这个例子作为一个题目来研究就好),代码如代码段4:
代码段4:
class A(object): def __init__(self): print "enter A" print "leave A"class B(object): def __init__(self): print "enter B" print "leave B"
class C(A): def __init__(self): print "enter C" super(C, self).__init__() print "leave C"
class D(A): def __init__(self): print "enter D" super(D, self).__init__() print "leave D" class E(B, C): def __init__(self): print "enter E" B.__init__(self) C.__init__(self) print "leave E"
class F(E, D): def __init__(self): print "enter F" E.__init__(self) D.__init__(self) print "leave F"
>>> f = F()
enter F enter E enter B leave B enter C enter D enter A leave A leave D leave C leave E enter D enter A leave A leave D leave F
object | / | A | / | B C D / / | E | / | F
二、走进Python的源码世界
我们尝试改写代码段4中的函数调用,但都没有得到我们想要的结果,这不得不使我们开始怀疑:我们对super的理解是否出了问题。
我们重新阅读了Python的官方文档,正如您所见,官方文档并没有详细的原理说明。到网络上去搜索,确实有人发现了同样的问题,并在一些论坛中讨论,但似乎并没有实质性的解答。既然,没有前人的足迹,我们只好走进Python的源码世界,去追溯问题的根源。
我们考查的是Python 2.3的源码(估计Python 2.4的源码可能也差不多)。首先,搜索关键字"super"。唯一找到的是bltinmodule.c中的一句:
SETBUILTIN("super", &PySuper_Type);
在typeobject.c中找到了PySuper_Type的定义:
代码段5:
PyTypeObject PySuper_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, /* ob_size */ "super", /* tp_name */ sizeof(superobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ super_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ super_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ super_getattro, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /* tp_flags */ super_doc, /* tp_doc */ super_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ super_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ super_descr_get, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ super_init, /* tp_init */ PyType_GenericAlloc, /* tp_alloc */ PyType_GenericNew, /* tp_new */ PyObject_GC_Del, /* tp_free */ };
再看superobject的定义:
代码段6:
typedef struct { PyObject_HEAD PyTypeObject *type; PyObject *obj; PyTypeObject *obj_type; } superobject;
代码段7:
static int super_init(PyObject *self, PyObject *args, PyObject *kwds) { superobject *su = (superobject *)self; PyTypeObject *type; PyObject *obj = NULL; PyTypeObject *obj_type = NULL; if (!PyArg_ParseTuple(args, "O!|O:super", &PyType_Type, &type, &obj)) return -1; if (obj == Py_None) obj = NULL; if (obj != NULL) { obj_type = supercheck(type, obj); if (obj_type == NULL) return -1; Py_INCREF(obj); } Py_INCREF(type); su->type = type; su->obj = obj; su->obj_type = obj_type; return 0; }
查找问题的切入点是为什么在类C中的super调用会切换到类D的初始化函数。于是在super_init中添加条件断点,并跟踪其后的Python代码。最终进入到super_getattro函数——对应于super对象访问名字__init__时的搜索操作。
代码段8(省略部分无关代码,并加入一些注释):
static PyObject * super_getattro(PyObject *self, PyObject *name) { superobject *su = (superobject *)self; int skip = su->obj_type == NULL; …… if (!skip) { PyObject *mro, *res, *tmp, *dict; PyTypeObject *starttype; descrgetfunc f; int i, n;starttype = su->obj_type; // 获得搜索的起点:super对象的obj_type mro = starttype->tp_mro; // 获得类的mro …… for (i = 0; i < n; i++) { // 搜索mro中,定位mro中的type if ((PyObject *)(su->type) == PyTuple_GET_ITEM(mro, i)) break; } i++; // 切换到mro中的下一个类 res = NULL; for (; i < n; i++) { // 在mro以后的各个命名空间中搜索指定名字 tmp = PyTuple_GET_ITEM(mro, i); if (PyType_Check(tmp)) dict = ((PyTypeObject *)tmp)->tp_dict; else if (PyClass_Check(tmp)) dict = ((PyClassObject *)tmp)->cl_dict; else continue; res = PyDict_GetItem(dict, name); if (res != NULL) { Py_INCREF(res); f = res->ob_type->tp_descr_get; if (f != NULL) { tmp = f(res, su->obj, (PyObject *)starttype); Py_DECREF(res); res = tmp; } return res; } } } return PyObject_GenericGetAttr(self, name); }
PyObject* tp_mro Tuple containing the expanded set of base types, starting with the type itself and ending with object, in Method Resolution Order.This field is not inherited; it is calculated fresh by PyType_Ready().
F E B C D A object
我们把代码段4改写为:
代码段9:
class A(object): def __init__(self): print "enter A" super(A, self).__init__() # new print "leave A"class B(object): def __init__(self): print "enter B" super(B, self).__init__() # new print "leave B"
class C(A): def __init__(self): print "enter C" super(C, self).__init__() print "leave C"
class D(A): def __init__(self): print "enter D" super(D, self).__init__() print "leave D" class E(B, C): def __init__(self): print "enter E" super(E, self).__init__() # change print "leave E"
class F(E, D): def __init__(self): print "enter F" super(F, self).__init__() # change print "leave F"
>>> f = F()
enter F enter E enter B enter C enter D enter A leave A leave D leave C leave B leave E leave F
三、延续的讨论
我们再重新看上面的类体系图,如果把每一个类看作图的一个节点,每一个从子类到父类的直接继承关系看作一条有向边,那么该体系图将变为一个有向图。不能发现mro的顺序正好是该有向图的一个拓扑排序序列。
从而,我们得到了另一个结果——Python是如何去处理多继承。支持多继承的传统的面向对象程序语言(如C++)是通过虚拟继承的方式去实现多继承中父类的构造函数被多次调用的问题,而Python则通过mro的方式去处理。
但这给我们一个难题:对于提供类体系的编写者来说,他不知道使用者会怎么使用他的类体系,也就是说,不正确的后续类,可能会导致原有类体系的错误,而且这样的错误非常隐蔽的,也难于发现。
四、小结
1. super并不是一个函数,是一个类名,形如super(B, self)事实上调用了super类的初始化函数,
产生了一个super对象;
2. super类的初始化函数并没有做什么特殊的操作,只是简单记录了类类型和具体实例;
3. super(B, self).func的调用并不是用于调用当前类的父类的func函数;
4. Python的多继承类是通过mro的方式来保证各个父类的函数被逐一调用,而且保证每个父类函数
只调用一次(如果每个类都使用super);
5. 混用super类和非绑定的函数是一个危险行为,这可能导致应该调用的父类函数没有调用或者一
个父类函数被调用多次。
本文向大家介绍python super函数使用方法详解,包括了python super函数使用方法详解的使用技巧和注意事项,需要的朋友参考一下 一、super函数简介 python内置函数super()主要用于类的多继承中,用来查找并调用父类的方法,所以在单重继承中用不用 super 都没关系;但是,使用 super() 是一个好的习惯。一般我们在子类中需要调用父类的方法时才会这么用; 二、sup
本文向大家介绍Python中的super()方法使用简介,包括了Python中的super()方法使用简介的使用技巧和注意事项,需要的朋友参考一下 子类里访问父类的同名属性,而又不想直接引用父类的名字,因为说不定什么时候会去修改它,所以数据还是只保留一份的好。其实呢,还有更好的理由不去直接引用父类的名字, 这时候就该super()登场啦—— 当然 Python 2 里super() 是一定要参数的
本文向大家介绍python中 * 的用法详解,包括了python中 * 的用法详解的使用技巧和注意事项,需要的朋友参考一下 1、表示乘号 2、表示倍数,例如: 打印结果(打印3次): hi hi hi 3、单个 * (1)、如:*parameter是用来接受任意多个参数并将其放在一个元组中。 (2)、函数在调用多个参数时,在列表、元组、集合、字典及其他可迭代对象作为实参,并在前面加 * 如 *
本文向大家介绍详解python中@的用法,包括了详解python中@的用法的使用技巧和注意事项,需要的朋友参考一下 python中@的用法 @是一个装饰器,针对函数,起调用传参的作用。 有修饰和被修饰的区别,‘@function'作为一个装饰器,用来修饰紧跟着的函数(可以是另一个装饰器,也可以是函数定义)。 代码1 结果1 It's funA 分析1 @funA 修饰函数定义def funC(),
本文向大家介绍java 关键字super详解及用法,包括了java 关键字super详解及用法的使用技巧和注意事项,需要的朋友参考一下 java 关键字super详解 一、super关键字 在JAVA类中使用super来引用父类的成分,用this来引用当前对象,如果一个类从另外一个类继承,我们new这个子类的实例对象的时候,这个子类对象里面会有一个父类对象。怎么去引用里面的父类对象呢?使用
本文向大家介绍python里 super类的工作原理详解,包括了python里 super类的工作原理详解的使用技巧和注意事项,需要的朋友参考一下 super 的工作原理如下: 其中,cls 代表类,inst 代表实例,上面的代码做了两件事: 获取 inst 的 MRO 列表 查找 cls 在当前 MRO 列表中的 index, 并返回它的下一个类,即 mro[index + 1] 当你使用 su