Re: [問題] 多重繼承以及super()

作者: djshen (djshen)   2018-08-10 01:55:01
※ 引述《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__
這就說明了為什麼輸出順序是這樣
作者: djshen (djshen)   2018-08-10 02:11:00
其實官方文件就有寫getattr和super的search order一樣
作者: izno (sl)   2018-08-11 00:34:00
感謝解答!!

Links booklink

Contact Us: admin [ a t ] ucptt.com