Python 深入了解super的用法
- Python 深入了解super的用法
- 帶着問題去學super
- super
- 兩種調用父類方法的差別
- 通過父類名直接調用父類方法
- super
- 代碼說明
- 總結
- 疑問
- 參考網址
轉載請标明出處(http://blog.csdn.net/lis_12/article/details/52870728).
帶着問題去學super
Python中既然可以直接通過父類名調用父類的方法為什麼還會存在super函數?
比如
class Child(Parent):
def __init__(self):
Parent.__init__(self)
這種方式與super(Child, self).__init__()有差別呢?
準備知識:
MRO 全稱 Method Resolution Order(方法解析順序),它代表了類繼承的順序.
注:如果不懂MRO,參考這篇文章(http://blog.csdn.net/lis_12/article/details/52859376)
super()
謹記:super 指的是 MRO 中的下一個類! 不代表父類!!!!!!!
例如:MRO = [A,B,C,D]
super(A,self).__init__()代表A的下一個類B的__init__().
super()的代碼實作:
def super(cls, inst):
mro = inst.__class__.mro() #擷取inst的MRO
return mro[mro.index(cls) + ] #擷取cls在MRO中的索引,然後加1
參數說明:
- inst 負責生成 MRO 的 list;
- 通過 cls 定位目前 MRO 中的 index, 并傳回 MRO[index + 1];
這兩件事才是 super 的實質,一定要記住!
兩種調用父類方法的差別
通過父類名直接調用父類方法
class D(object):
def __init__(self):
print 'enter D'
object.__init__(self)
print 'leave D'
class C(D):
def __init__(self):
print 'enter C'
D.__init__(self)
print 'leave C'
class B(D):
def __init__(self):
print 'enter B'
D.__init__(self)
print 'leave B'
class A(B,C):
def __init__(self):
print 'enter A'
B.__init__(self)
C.__init__(self)
print 'leave A'
if __name__ == '__main__':#主程式
a = A()
print A.__MRO__
'''
result:
enter A
enter B
enter D
leave D
leave B
enter C
enter D 初始化了兩次--!
leave D
leave C
leave A
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <type 'object'>)
'''
super()
#!/usr/bin/python
# -*- coding: utf-8 -*-
class D(object):
def __init__(self):
print 'enter D'
super(D,self).__init__()
self.x = 'D'
print 'leave D'
def fun(self):
print 'D fun()',self.x
class C(D):
def __init__(self):
print 'enter C'
super(C,self).__init__()
self.x = 'C'
print 'leave C'
def fun(self):
print 'C fun()',self.x
class B(D):
def __init__(self):
print 'enter B'
super(B,self).__init__()
print 'leave B'
class A(B,C):
def __init__(self):
print 'enter A'
super(A,self).__init__()
self.x = 'A'
print 'leave A'
if __name__ == '__main__':#主程式
a = A()
print A.__MRO__
a.fun() #按照MRO查找fun()方法,找到後停止查找,self.x = 'A'最後執行的是以self.x = A,如果把self.x = 'A'放到super(A,self).__init__(),self.x = 'C'就是最後執行的喽.
'''
result:
enter A
enter B
enter C
enter D
leave D D隻初始化一次
leave C
leave B
leave A
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <type 'object'>)
C fun() A
'''
代碼說明
通過父類名調用父類中的方法沒啥可說的,順序執行就好,然後就重複調用了父類D的__init__()方法- -!…重點說super.
使用super(A,self).__init__()執行流程:
- 首先進入類A的__init__()函數,列印’enter A’,當執行到super處,super(A,self)擷取MRO中A的下一個類B,并傳回,調用B.__init__();
- 進入B.__init__(),列印 ‘enter B’,當執行到super處,super(B,self)擷取MRO中B的下一個類C,并傳回,調用C.__init__();
- 進入C.__init__(),列印 ‘enter C’,當執行到super處,super(C,self)擷取MRO中C的下一個類D,并傳回,調用D.__init__();
- 進入D.__init__(),列印 ‘enter D’,當執行到super處,super(D,self)擷取MRO中D的下一個類object,并傳回,調用object.__init__();
- object.__init__()執行完成後,開始執行D.__init__()->C.__init__()->B.__init__()->A.__init__()中未執行的代碼,依次列印leave D C B A;
總結
使用super()優點:
- 保證不會重複調用父類的__init__()函數;
- 代碼更好維護,如果把父類的名字改了以後,使用類名調用方法還需要改類名,代碼可維護性太差,使用super則能很好的解決這些問題;
使用super()注意事項:
super要麼全用,要麼全不用,哪怕父類是object也要用super,千萬不要混用.
混用帶來的坑:
class D(object):
def __init__(self):
print 'enter D'
super(D,self).__init__()
class C(D):
def __init__(self):
print 'enter C'
super(C,self).__init__()
class B(D):
def __init__(self):
print 'enter B'
D.__init__(self)
class A(B,C):
def __init__(self):
print 'enter A'
super(A,self).__init__()
if __name__ == '__main__':#主程式
a = A()
print A.__MRO__
'''
result:
enter A
enter B
enter D
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <type 'object'>)
'''
從以上代碼可知,沒有調用C的__init__()方法.
__init__()調用順序:A->B->D->object,繞過了C.
疑問
#!/usr/bin/python
# -*- coding: utf-8 -*-
class Root(object):
def __init__(self):
print("this is Root")
class B(Root):
def __init__(self):
print("enter B")
# print(self) # this will print <__main__.D object at 0x...>
super(B, self).__init__()
print("leave B")
class C(Root):
def __init__(self):
print("enter C")
super(C, self).__init__()
print("leave C")
class D(B, C):
pass
d = D()
print(d.__class__.__MRO__)
'''
enter A D中沒有重定義__init__()函數是怎麼調用的呢?
enter B
enter D
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <type 'object'>)
'''
個人了解:D中預設的__init__()使用了super()……如果有大神知道原因的話求指導,謝謝…
參考網址
- https://www.laike9m.com/blog/li-jie-python-super,70/
- https://rhettinger.wordpress.com/2011/05/26/super-considered-super/
- http://blog.csdn.net/lis_12/article/details/52859376