天天看點

了解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()
           
了解Python中的Super

分析:

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()的一些用法