1. 程式人生 > 程式設計 >Python類的繼承super相關原理解析

Python類的繼承super相關原理解析

看了網上許多關於super、mro、C3的介紹感覺沒有一份很容易初學者理解的文件,直接看C3演算法的話,比較難理解,也沒必要,如果掌握一套規律的話,會輕鬆許多。我將網上這些博主的文章進行一個梳理總結,最後形成一套實用的關於super、mro、C3的理解介紹。

1、super

super()是一種將子類與父類聯絡起來的一種方法,子類通過繼承父類,可以使用父類的方法和屬性,也可以自己定義屬於自己的方法和屬性。super方法主要用在多繼承中,在單繼承時直接呼叫父類方法即可

下面這個是SGD原始碼的一部分,根據這份原始碼,

class SGD(Optimizer):
  def __init__(self,params,lr=required,momentum=0,dampening=0,weight_decay=0,nesterov=False):
    defaults = dict(lr=lr,momentum=momentum,dampening=dampening,weight_decay=weight_decay,nesterov=nesterov)
    if nesterov and (momentum <= 0 or dampening != 0):
      raise ValueError("Nesterov momentum requires a momentum and zero dampening")
    super(SGD,self).__init__(params,defaults)
  def __setstate__(self,state):
    super(SGD,self).__setstate__(state)
    for group in self.param_groups:
      group.setdefault('nesterov',False)

這是SGD類中的程式碼

有2點需要補充說明:

1、super聯絡父類的時候,需要呼叫父類的方法,包括所帶的形參寫完整,子類不夠的形參需要額外加上

2、super聯絡父類的時候,不只是可以呼叫__init__,而且還可以呼叫父類其他的方法

3、python3可以寫成super().__init__()這種寫法了。

4、類都預設繼承object類

另外,在super的使用過程中,還需要注意初始化對繼承的影響:

1、子類繼承父類,但不執行__init__方法,那麼會自動繼承父類屬性。

2、子類繼承父類,執行了__init__方法,且不呼叫super初始化父類建構函式,那麼子類不會自動繼承父類屬性。

3、子類繼承父類,執行了__init__方法,且呼叫了super初始化了父類的建構函式,那麼子類會繼承父類屬性。

2、mro

Python的MRO,方法解析順序,即在呼叫方法時,會對當前類以及所有的基類進行一個搜尋,以確定該方法之所在,而這個搜尋的順序就是MRO。然後python會按照這個順序去執行類之間的呼叫問題。

直接上例子

class A1():
  def __init__(self):
    print('A1')
    super().__init__()
class A2():
  def __init__(self):
    print('A2')
    super().__init__()
class A3():
  def __init__(self):
    print('A3')
    super().__init__()
class B1(A1,A2):
  def __init__(self):
    print('B1')
    super().__init__()
class B2(A2):
  def __init__(self):
    print('B2')
    super().__init__()
class B3(A2,A3):
  def __init__(self):
    print('B3')
    super().__init__()
class C1(B1):
  def __init__(self):
    print('C1')
    super().__init__()
class C2(B1,B2):
  def __init__(self):
    print('C2')
    super().__init__()
class C3(B2,B3):
  def __init__(self):
    print('C3')
    super().__init__()
class D(C1,C2,C3):
  def __init__(self):
    print('D')
    super().__init__()
d = D()
print(D.__mro__)

輸出如下:

Python類的繼承super相關原理解析

(<class '__main__.D'>,<class '__main__.C1'>,<class '__main__.C2'>,<class '__main__.B1'>,<class '__main__.A1'>,<class '__main__.C3'>,<class '__main__.B2'>,<class '__main__.B3'>,<class '__main__.A2'>,<class '__main__.A3'>,<class 'object'>)

那麼這個程式是按怎麼個順序依次去執行那些方法呢,就是按照MRO中的順序。

Python類的繼承super相關原理解析

那麼這個順序如何自己手寫出來呢?這就是C3演算法,用於計算出MRO,得出執行順序

3、c3演算法

但是我在這裡想告訴大家如何根據C3演算法找出規律,從而自己寫出順序

還是上手例子,根據上面這個圖,我們可以列出下面這個表,這個表示網上一個博主做的,可以說做出這張表就是做出了答案,那這張表“實際取出的類”怎麼推出來的呢?我拿過來提煉出一些比較直接的規律

Python類的繼承super相關原理解析

規律1:預查詢父類按左邊優先原則,比如第一行,當前類D的預查詢父類選最左邊的C1,而不是C2、C3。

規律2:當“預查詢父類是否還有其他子類?同時又是最底層查詢類的父類或父父類、父父父...類”沒有的時候,直接選取預查詢父類作為答案。當這項有類的時候,若“最底層的未查詢父類”還有,則優先選它,若沒有了,則選取這項類作為答案。

規律3:當預查詢父類是object,只要最底層的未查詢父類還有,就選這個最底層的未查詢父類。若沒有,則“預查詢父類是否還有其他子類?同時又是最底層查詢類的父類或父父類、父父父...類”有類的時候,就選這個類,沒有的話,答案就是object

另外,

如何去畫圖:

1、子類永遠畫在父類的下面,並用有向箭頭指向父類

2、遇到多繼承則按程式碼中繼承列表的順序從左往右寫。如果有多個子類繼承了同一個父類,那麼這個父類則放在它能夠出現的所有位置中最左的位置。需遵循圖裡面的廣度優先原則進行遍歷(在廣度優先原則的前提下又優先遍歷左邊的):

練習:

下面這個是網上的一份程式碼,看懂上面的規律以及如何畫有向圖之後,就可以順利得出MRO的值了

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

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

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


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

class G(E,F):
  def __init__(self):
    print('G')
    super().__init__()

首先,畫圖

Python類的繼承super相關原理解析

然後列表

當前類最底層未查詢的父類預查詢的父類預查詢父類是否為object預查詢父類是否有其他子類,同時又是最底層查詢類的父類、父父類實際取出的類GE、FEnonoEEFBnonoBBFAno有,CFFnoCnonoCCnoAno有,DDDnoAnonoAAnoobjectyesnoobject

答案:

mro:GEBFC

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。