理解Python中的Super

Python中可以直接通過調用父類名調用父類方法,在多重繼承中,使用super()是一個很好的習慣。

super的本質返回的是MRO的下一個類

def super(cls, inst):
    mro = inst.__class__.mro()
    return mro[mro.index(cls) + 1]

兩個參數 cls 和 inst 分別做了兩件事: 

  1. inst 負責生成 MRO 的 list 
  2. 通過 cls 定位當前 MRO 中的 index, 並返回 mro[index + 1] 

這兩件事纔是 super 的實質,一定要記住! 

MRO 全稱 Method Resolution Order,它代表了類繼承的順序。

很重要的一句話:在 MRO 中,基類永遠出現在派生類後面,如果有多個基類,基類的相對順序保持不變。

 

放幾個例子

例1:

class A:
 def print(self):
  print('A')
 
class B:
 def print(self):
  print('B')
 
class C(A):
 def print(self):
  print('C')
  super().print()
 
C().print()
# C
# A

例2:

class A(object):
    def __init__(self):
        print('A')

class B(A):
    def __init__(self):
        super(B, self).__init__()
        print('B')

class C(object):
    def __init__(self):
        print('C')

class D(C):
    def __init__(self):
        super(D, self).__init__()
        print('D')

class E(B, D):
    def __init__(self):
        super(E, self).__init__()
        super(A, self).__init__()
        print('E')

e = E()

  

分析:

print(e.__class__.__mro__)得到MRO序列(<class '__main__.E'>, <class '__main__.B'>, <class '__main__.A'>, <class '__main__.D'>, <class '__main__.C'>, <class 'object'>)

super(E, self).__init__()在上述MRO中找E後續的類爲B,根據順序找到B類繼承類A,最後輸出A、B

super(A, self).__init__()在MRO中找A後續的類爲D,根據順序找到D類繼承類C,最後輸出C、D

 

例3:(來自參考文獻2)

class A:
    def __init__(self):
        print("A", end=" ")
        super().__init__()


class E:
    def __init__(self):
        print("E", end=" ")
        super().__init__()


class B(E):
    def __init__(self):
        print("B", end=" ")
        super().__init__()


class D:
    def __init__(self):
        print("D", end=" ")
        super().__init__()


class C(A, B, D):
    def __init__(self):
        print("C", end=" ")
        A.__init__(self)    # A B E D
        D.__init__(self)    # D
        B.__init__(self)    # B E D


print("MRO:", [x.__name__ for x in C.__mro__])
print(C())

 

MRO: ['C', 'A', 'B', 'E', 'D', 'object']
C A B E D D B E D <__main__.C object at 0x1040340b8>

分析:

當執行A.__init__(self)這行代碼的時候,self指向了c實例對象,然後打印出A,當調用super().__init__()這行代碼的時候,實際相當於super(A, self).__init__()的調用, super(A, self)的會在self.mro中查找排在A後邊的類,這樣就實現了繼承鏈的調用了。


例4:(來自參考文獻1)

class root(object):
    def __init__(self):
        print('this is root')
class B(root):
    def __init__(self):
        print('enter B')
        super().__init__()
        print('leave B')
class C(root):
    def __init__(self):
        print('enter C')
        super().__init__()
        print('leave C')
class D(B, C):
    pass

d = D()
print(d.__class__.__mro__)

 

enter B
enter C
this is root
leave C
leave B
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.root'>, <class 'object'>)

分析:

super().__init__()這句的self還是D,所以從MRO序列中一次讀取。之所以B的init會被調用是因爲D中沒有定義__init__,所以會在MRO中的下一個類中找到定義的__init__。

 

所以,使用super,如果你要改變子類繼承的父類(由A改爲B),在例子1中只需要修改一行代碼(class C(A): -> class C(B))即可,而不需要在class C的大量代碼中去查找、修改基類名,另外一方面代碼的可移植性和重用性也更高。

 

參考文獻:

1. 理解 Python super(裏面提到了比較好的一篇文章)

2. python中super()的一些用法 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章