《Python面試每日一題》之super

問題描述:

在Python中,當子類引用父類的函數時,有幾種方式?闡明不同方式的區別和MRO機制。

答:

子類引用父類的函數時有三種方法。

1. 父類名.函數名(self, 參數)
2. super().函數名(參數)
3. super(類名).函數名(參數)

在這裏插入圖片描述
Python支持多繼承機制,即一個類可以有多個父類。當使用第一種方式調用父類的函數時,父類名寫誰,代碼就去調用哪個父類的函數,當兩個父類同時繼承於一個父類時,如圖所示,這種調用方式就會產生內存的冗餘。

# 定義一個父類
class father(object):
    def __init__(self,name,*args,**kwargs):
        self.name = name
        print("Father類調用1次" )
        
# 定義第一個子類1,繼承於父類
class son_1(father):
    def __init__(self,name,gender,*args,**kwargs):
        father.__init__(self,name,*args,**kwargs)
        self.gender = gender
        
# 定義第二個子類2,野繼承於父類
class son_2(father):
    def __init__(self,name,age,*args,**kwargs):
        father.__init__(self,name,*args,**kwargs)
        self.age = age
        
# 定義一個“孫子類”,同時繼承於子類1和子類2
class grandson(son_1,son_2):	
    def __init__(self,name,gender,age,language,*args,**kwargs):
        son_1.__init__(self,name,gender,*args,**kwargs)
        son_2.__init__(self,name,age,*args,**kwargs)
        self.language = language
       
# 創建一個“孫子類”對象
tomato = grandson("tomato","male","21","python")

當創建一個grandson對象時,會自動調用grandson類的init函數,初始化對象。
因爲在grandson類的init函數中使用了 父類名.函數名(self,參數) 的方式調用son_1和son_2的init函數,且son_1和son_2同時也用了此方式調用father類的init函數,
所以father類的init函數會被機械的執行兩次,造成內存冗餘。
運行上述代碼,結果如下。

Father類調用1次
Father類調用1

這種多繼承父類同時繼承於一個父類的情況,使用 super().函數名(參數) 的方式調用父類函數,就不會造成內存冗餘了。
這是因爲MRO機制在發揮着作用,在Python中存在一種算法叫C3算法,其結果是生成一個元組,這個元組會存在類的__mro__內置屬性裏面,元組中元素的順序就是調用super函數時的調用順序。
我們將上述代碼修改一下,將所有的 父類名.函數名(self,參數) 改爲 super().函數名(參數)

class father(object):
    def __init__(self,name,*args,**kwargs):
        self.name = name
        print("Father類調用1次" )

class son_1(father):
    def __init__(self,name,gender,*args,**kwargs):
        self.gender = gender
        super().__init__(name,*args,**kwargs)

class son_2(father):
    def __init__(self,name,age,*args,**kwargs):
        self.age = age
        super().__init__(name,*args,**kwargs)

class grandson(son_1,son_2):
    def __init__(self,name,gender,age,language,*args,**kwargs):
        self.language = language
        super().__init__(name,gender,age,*args,**kwargs)

tomato = grandson("tomato","male","21","python")
print(grandson.__mro__)

運行上述代碼,結果如下:

Father類調用1(<class '__main__.grandson'>, <class '__main__.son_1'>, <class '__main__.son_2'>, <class '__main__.father'>, <class 'object'>)

這時,Father類的init函數只被調用了一次。
需要注意的是,在son_1類的init函數中使用super().函數名(參數) 時,此時線程不是執行的Father類的init函數,而是執行的son_2的init函數,因爲在__mro__內置屬性的元組中,son_2在son_1的後面。
簡而言之,MRO機制就是一種基於C3算法的父類調用順序。它保證了Python在多繼承中,每個父類的函數只會執行一次,防止了重複調用。

若有需要,可點擊前往瞭解何爲C3算法:詳解C3算法

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