在我们的印象中,对于super(b, self).__init__()是这样理解的:super(b, self)首先找到b的父类(就是类a),然后把类b的对象self转换为类a的对象,然后“被转换”的类a对象调用自己的__init__函数。
有一天某同事设计了一个相对复杂的类体系结构(我们先不要管这个类体系设计得是否合理,仅把这个例子作为一个题目来研究就好),代码如下
代码段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
明显地,类a和类d的初始化函数被重复调用了2次,这并不是我们所期望的结果!我们所期望的结果是最多只有类a的初始化函数被调用2次——其实这是多继承的类体系必须面对的问题。我们把代码段4的类体系画出来,如下图:
object
| \
| a
| / |
b c d
\ / |
e |
\ |
f
按我们对super的理解,从图中可以看出,在调用类c的初始化函数时,应该是调用类a的初始化函数,但事实上却调用了类d的初始化函数。好一个诡异的问题!
也就是说,mro中记录了一个类的所有基类的类类型序列。查看mro的记录,发觉包含7个元素,7个类名分别为:
f e b c d a object
从而说明了为什么在c.__init__中使用super(c, self).__init__()会调用类d的初始化函数了。 ?
我们把代码段4改写为:
代码段5:
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
可见,f的初始化不仅完成了所有的父类的调用,而且保证了每一个父类的初始化函数只调用一次。
小结
1. super并不是一个函数,是一个类名,形如super(b, self)事实上调用了super类的初始化函数,
产生了一个super对象;
2. super类的初始化函数并没有做什么特殊的操作,只是简单记录了类类型和具体实例;
3. super(b, self).func的调用并不是用于调用当前类的父类的func函数;
4. python的多继承类是通过mro的方式来保证各个父类的函数被逐一调用,而且保证每个父类函数
只调用一次(如果每个类都使用super);
5. 混用super类和非绑定的函数是一个危险行为,这可能导致应该调用的父类函数没有调用或者一
个父类函数被调用多次。
一些更深入的问题:各位可以看到,print f.__mro__时发现里面元素的顺序是 f e b c d a object,这就是f的基类查找顺序,至于为什么是这样的顺序,以及python内置的多继承顺序是怎么实现的,这涉及到mro顺序的实现,
