※ 引述《lv100 (Tsl)》之铭言:
: 各位python版的前辈大家好
: 最近小弟在自学python
: 到了多重继承的这边有点小疑问
: 程式码如下:
: class Base(object):
: def __init__(self):
: print ("enter Base")
: print ("leave Base")
: class A(Base):
: def __init__(self):
: print ("enter A")
: super(A, self).__init__()
: print ("leave A")
: class B(Base):
: def __init__(self):
: print ("enter B")
: super(B, self).__init__()
: print ("leave B")
: class C(A, B):
: def __init__(self):
: print ("enter C")
: super(C, self).__init__()
: print ("leave C")
: c = C()
: 输出的是:
: enter C
: enter A
: enter B
: enter Base
: leave Base
: leave B
: leave A
: leave C
: 我知道多重继承中
: super()调用的顺序是根据MRO列表的顺序
: 所以到leave Base都可以理解
: 疑问的点在于leave B->leave A->leave C的顺序
: 想请问这边程式是怎么运行才会是如输出的顺序
: 感谢各位的解答
翻了一下source code
相关的应该在这
https://github.com/python/cpython/blob/master/Objects/typeobject.c
当你call super(C, self)的时候 会产生一个super object
印出来长这样 <super: <class 'C'>, <C object>>
实验看看在每个class的__init__里面的super(...).__init__()后面
都加上 print(super(X, self))
call C()的时候印出的是
<super: <class 'Base'>, <C object>>
<super: <class 'B'>, <C object>>
<super: <class 'A'>, <C object>>
<super: <class 'C'>, <C object>>
call B()的时候印出
<super: <class 'Base'>, <B object>>
<super: <class 'B'>, <B object>>
再来看source code里面的 super_init function 大概就知道他在做什么
最后面的地方
Py_XSETREF(su->type, type);
Py_XSETREF(su->obj, obj);
Py_XSETREF(su->obj_type, obj_type);
以<super: <class 'A'>, <C object>>来讲
su->type 就是 A
su->obj_type 就是 C
以上讲的是 super(C, self) 发生的事
接下来要从这 super(C, self) 里面拿 __init__ 这个attribute
相关的code在 super_getattro function里面
这边没什么问题 就是拿到 A.__init__
但是在要从 super(A, self) 里面拿 __init__ 的时候就不太一样了
前面看到现在的 super(A, self) 其实是 <super: <class 'A'>, <C object>>
进到 super_getattro 以后
starttype = su->obj_type; // 拿到 C
mro = starttype->tp_mro; // 拿到 [C, A, B, Base, object]
// 找 A 在 mro 里的下一个
for (i = 0; i+1 < n; i++) {
if ((PyObject *)(su->type) == PyTuple_GET_ITEM(mro, i))
break;
}
i++;
剩下的code就是从 C 的 mro 的第 i 个里面取attribute
从上面可以看到
<super: <class 'A'>, <C object>>.__init__
拿到的其实是 B.__init__
然后 <super: <class 'B'>, <C object>>.__init__
拿到的是 B 在 C 的 mro 里面的下一个
也就是 Base.__init__
这就说明了为什么输出顺序是这样