首先提一下,經典類和新式類。在Python2中,如果定義類的方式是 class MyClass: 那麼該類叫做經典類,如果定義類的方式爲class MyClass(object): 那麼該類爲新式類。在Python3中,上面兩種方式定義出來的類都叫新式類。本文是基於新式類來進行講解的。
正文:
Python 中的 super() 是用於調用父類(或父類的父類...)方法的 函數,主要用於多繼承,單繼承問題不大。下面是一個多繼承實例,繼承關係爲菱形繼承, 仔細觀察下面三個實例:
#coding=utf-8
#實例一:
class A(object):
def __init__(self):
print("class ---- A ----")
class B(A):
def __init__(self):
print("class ---- B ----")
super(B, self).__init__()
class C(A):
def __init__(self):
print("class ---- C ----")
super(C, self).__init__()
class D(B, C):
def __init__(self):
print("class ---- D ----")
super(D, self).__init__()
d = D()
'''
#輸出結果:
class ---- D ----
class ---- B ----
class ---- C ----
class ---- A ----
'''
#coding=utf-8
#實例二: 更改一下類D的super函數:
class D(B, C):
def __init__(self):
print("class ---- D ----")
super(B, self).__init__()
d = D()
'''
#輸出結果:
class ---- D ----
class ---- C ----
class ---- A ----
'''
# 實例三: 再更改一下類D的super函數:
class D(B, C):
def __init__(self):
print("class ---- D ----")
super(C, self).__init__()
d = D()
'''
# 輸出結果:
class ---- D ----
class ---- A ----
'''
如果你認爲 super()函數就是 調用父類的方法,那你可能想不通後面兩個實例。原因是,super
和父類沒有實質性的關聯。如果想要搞懂super()函數的運行原理,那一定要先搞懂 __mro__ 屬性, mro 是Method Resolution Order,中文方法解析順序。單繼承中super()函數使用比較簡單的原因 也是因爲 mro 比較簡單,多繼承的mro就稍微複雜了,總之 mro的目的就是 按照一定順序,保證父類的函數只調用一次。
我們來先打印一下 它的屬性值,再次修改類D的super函數:
class D(B, C):
def __init__(self):
print(D.__mro__)
print("class ---- D ----")
super(D, self).__init__()
d = D()
'''
#輸出結果:
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
class ---- D ----
class ---- B ----
class ---- C ----
class ---- A ----
'''
從打印結果可以看出,mro 是一個元組,類D的mro順序是 D -> B -> C -> A -> object。也就是說,如果在類D中 使用super()函數時給傳入的第一個參數是D,那麼super函數就會在 mro 裏從D的上一級開始查找,它的上一級是B, 那麼super(D, self).__init__() 就調用B的__init__()函數,B的__init__()函數裏又調用了B的super()函數,super(B, self).__init__(), 那就再從B的上一級再開始查找,B的上一級是C, 以此類推,然後是A,最後是object。於是實例一也就解釋清楚了。
實例二中 類D的super()方法第一個參數傳入的是 B ,那麼根據mro順序開始查找,B的上一級是C,C的上一級是A,所以實例二的打印順序是 D - > C -> A
實例三也就不難解釋了。
最後再講一下 super()函數的參數,該函數需要兩個參數,第一個是類名,第二個一般都是self,但偶爾也有cls的情況,在:Python 3 可以使用直接使用 super().xxx 代替 super(Class, self).xxx, 比如:
class D(B, C):
def __init__(self):
print(D.__mro__)
print("class ---- D ----")
super().__init__()
d = D()
'''
#輸出結果:
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
class ---- D ----
class ---- B ----
class ---- C ----
class ---- A ----
'''
再回到文章的開始,談一下經典類和新式類:
經典類的MRO方法是採用 從左至右的深度優先遍歷 的算法,重複留前者
按照深度遍歷,其順序爲 [D, B, A, object, C, A, object],重複者只保留前面一個,因此變爲 [D, B, A, object, C]
新式類的MRO方法是採用 從左至右的深度優先遍歷 的算法,重複留後者
按照深度遍歷,其順序爲 [D, B, A, object, C, A, object],重複者只保留後面一個,因此變爲 [D, B, C, A, object]