python繼承(super()、多繼承、鑽石繼承)

python繼承簡介

Java只支持單繼承多接口模式。Python支持多繼承模式。
在多繼承模式中有個難題就是鑽石繼承也叫做菱形繼承
下圖就是鑽石繼承的例子。
一般來說,Leaf類初始化時會初始化Medium1類和Medium2類,然後Medium1類初始化時會初始化Base類,Medium2類初始化時也會初始化Base類,所以這就導致Leaf類初始化時對Base類進行了兩次初始化操作。python是通過super()來解決這個問題的。
在這裏插入圖片描述

還是首先來點乾貨,子類初始化時在__init__方法裏如何調用父類的初始化方法。有如下兩種方法

  • 父類名稱.__init__(self,參數1,參數2,...)
  • super(子類,self).__init__(參數1,參數2,....)

關於super()我們可以通過help(super)函數查看其使用說明。
在這裏插入圖片描述
super()就等價於super(__class__, <first argument>),即super(當前class, self)
super(type, obj) -> bound super object; requires isinstance(obj, type) ,其中第一個參數是開始尋找父類的起始點(起始但不包括),第二個參數是需要一個對應第一個type的實例,即滿足isinstance(obj,type),這個方法將返回第一個滿足繼承關係的類,尋找順序遵從type.__mro__屬性順序(mro將類的樹形繼承關係變成了一個list)

下面展示一個例子,類之間的關係圖如下所示。
Student類分別繼承於Person類和IdCard類,Person類又繼承於Human類,當然Human類和IdCard類都會繼承於object類(這裏就不顯示出來了)。
可通過Student.mro()查看Student類的繼承關係(變成了list)。
在這裏插入圖片描述

特別注意,由於多繼承的緣故,__init__方法的參數最好是寫成可選參數。
Student類的__init__方法中super(Human, self).__init__(*args)IdCard.__init__(self, *args)是等價的,這也可以從mro列表順序看出來。Student類的__init__方法也說明了當子類繼承多個父類時,如果有需要的話,可以在子類的__init__方法顯示調用多個父類的__init__方法。

# -*- coding: UTF-8 -*-

class Human(object):

    def __init__(self, sex):
        print("Human init start")
        self._sex = sex
        print("Human init end")

    def __str__(self):
        return "Human sex is {}".format(self._sex)

class Person(Human):

    def __init__(self, name, age, *args):
        print("Person init start")
        self._name = name
        self._age = age
        super().__init__(args[0])
        print("Person init end")


class IdCard(object):

    def __init__(self, *args):
        print("IdCard init start")
        self._id_card = args[-1]
        print("IdCard init end")

class Student(Person, IdCard):

    def __init__(self, grade, *args):
        print("Student init start")
        self._grade = grade
        super().__init__(*args)
        print(__class__)
        # super(Human, self).__init__(*args)  # 從Human開始起查找,但是不包括起點
        IdCard.__init__(self, *args)  # 這種方法也可以
        print("Student init end")

    def __str__(self):
        s = "sex={}\tname={}\tage={}\tgrade={}\tidCard={}".format(self._sex, self._name, self._age, self._grade, self._id_card)
        return s

# mro是method resolution order, 表示了類繼承體系中的成員解析順序
# 將繼承的樹關係變成了一個list
# [<class '__main__.Student'>, <class '__main__.Person'>, <class '__main__.Human'>, <class '__main__.IdCard'>, <class 'object'>]
print(Student.mro())
s = Student(100, "patrick", 26, "male", "programmer")
print(s)

運行結果如下
在這裏插入圖片描述

在設計類時,類的繼承關係一定要設計好設計清楚,要不然真是個大坑,特別是多繼承條件下!!!

這裏需要再次說明的是,__init__方法只是對實例對象做了屬性的初始化,並不等同於Java裏的new Student(),在python裏是通過__new__方法創建對象的,創建對象成功後再調用__init__方法做屬性的初始化。
在創建對象時可以通過元類來定製化創建,動態地給對象添加屬性或方法,這裏就可以參考python面向對象編程中的元類使用

參考網址

python如何解決鑽石繼承難題
python菜鳥教程----子類如何繼承父類初始化函數說明
python中關於子類調用父類初始化函數的說明

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