天天看點

Python_類的繼承

1.類的繼承關系和生活中父親、兒子、孫子之間的關系一樣,Python中若A類繼承B類,則A類稱之為子類,B類稱之為父類(也稱為基類)。

2.類的繼承方式分為:單繼承、多繼承兩種;

類的單繼承是指,A類隻繼承一個父類B,如下圖所示:

Python_類的繼承

類的多繼承是指,A類可以繼承多個父類,如下圖所示:

Python_類的繼承

3.類繼承的順序

如果子類繼承一個或多個父類時,子類屬性是任何調用的呢?見下圖執行個體,我們定義的患者類繼承了醫院類和衛生局類,問題1.患者類和醫院類中具有登記的方法(regpatien),此時子類調用該方法時是怎樣調用的呢?問題2.子類中沒有addr的屬性,而兩個父類中存在addr屬性,此時子類在調用addr屬性時能否正常調用?如果能正常調用,調用的又是哪個父類的addr屬性?

問題1.見下圖:

Python_類的繼承

問題2.1.如果子類繼承父類的順序為先Hospital後Healthbureau,如下圖所示:

Python_類的繼承

問題2.2.如果子類繼承父類的順序為先Healthbureau後Hospital,如下圖所示:

Python_類的繼承

總結:1.子類繼承父類時,在子類進行屬性調用的順序為:先查找自己的屬性字典,若自己的屬性字典中無該屬性,則會依次按照繼承父類的順序來依次查找父類的屬性字典;2.子類繼承父類,當父類和子類均有相同的屬性時,子類并不會影響父類的屬性。總結起來就是:按繼承的順序來依次查詢屬性,一旦查到則停止;子類和父類的屬性互相獨立,互不影響;子類可以調用父類的屬性,反之不行;

該部分的代碼塊為:

class Hospital():
    "醫院類-->父類"
    addr = "醫院位址為:無錫市解放路"
    depertment = ["超聲科","放射科","内鏡科"]
    def __init__(self,name,type,price):
        self.name = name
        self.type = type
        self.price = price
    def regpatien(self):
        print("--->>醫院正在登記患者")

class Healthbureau():
    "衛生局類"
    addr = "衛生局類位址為:無錫市中心大夏"
    def __init__(self,heigh,size):
        self.heigh =heigh
        self.size =size

# class Patient(Hospital,Healthbureau):   #多繼承,先Hospital後Healthbureau
class Patient(Healthbureau,Hospital):   #多繼承,先Healthbureau後Hospital
    "患者類"
    def __init__(self,patientname,age,sex):
        self.patientname = patientname
        self.age = age
        self.sex =sex
    def regpatien(self):
        print("--->>患者已經成功成功")
    def tohispital(self):
        print("%s去%s檢查"%(self.patientname,Hospital.depertment[2]))

#子類執行個體化
patient1 = Patient("李明",24,"男")
#父類Hospital執行個體化
hospital = Hospital("無錫市人民醫院","江蘇省無錫市人民大道","三甲")
#子類執行個體調用regpatien方法
patient1.regpatien()
#父類Hospital調用regpatien方法
hospital.regpatien()
#子類執行個體調用addr方法
print(patient1.addr)           

複制

深度解析類繼承順序

如何解析多層繼承關系?

多層繼承在python2和python3中解析的順序不同,python2中是深度優先的原則,python3中是以廣度優先的原則。繼承順序見下圖:

Python_類的繼承
Python_類的繼承

繼承原理:python到底是如何實作繼承順序的呢?對于你定義的每一個類,python會計算出一個方法解析順序(MRO)清單,這個MRO清單就是一個簡單的所有基類的線性順序清單。

為了實作繼承,python會在MRO清單上從左到右查找基類,直到找到第一個比對這個屬性的類為止。而這個MRO清單的構造是通過一個C3線性化算法來實作的。這個算法實際上就是合并所有父類的,MRO清單并遵循如下三條準則:

① 子類會由于父類被檢查;

② 多個父類會根據他們在清單中的順序被檢查;

③ 如果對于下一個類存在兩個合法的選擇,應選擇第一個父類;

為此我們可以直接使用子類的mro或者mro方法來查詢它自身的繼承順序,如下圖所示:

Python_類的繼承

4.接口繼承

從上面例子中我們可以看出,類的繼承有2種含義,一是:子類繼承基類的方法,并作出自己的擴充或改變(基類代碼的重用);二是:聲明某個子類相容于某基類,父類定義一個接口類,子類繼承接口類,并且實作接口類中定義的方法。

實踐中,繼承的第一種含義常常不建議使用,它是利小于弊,會出現代碼的強耦合,不利于後面的維護和擴充;但接口的第二中含義十分重要,又稱之為’接口繼承’。

接口繼承實質上是:“要求做出一個抽象,這個抽象規定了一個相容接口,使得外部調用者無需了解具體細節,可一視同仁的處理實作了特定接口的所有對象。”這種在程式設計上稱之為歸一化。

例如:定義一個學校的基類,學校有不能的課程、學校有不同的活動,但是幼稚園的課程隻是簡單的字母識别、數數等,而大學的課程包含了微積分、專業課程等。如下圖所示,通過接口的繼承來實作上面的要求:

Python_類的繼承

如果Kindergarten類或College類中沒有定義course或activity函數時,執行個體化直接報錯,如下圖所示:

Python_類的繼承

是以,接口繼承就是在基類中定義子類要實作的方法名稱(使用@abc.abstractclassmethod來裝飾該函數,但它并未無實際功能),這樣繼承它的子類就必須要自定義這個函數功能,若子類沒有該函數,則開始執行個體化就會報錯。

這樣我們如果知道某些類要實作某些相同名稱但功能不能的函數時,就可以先定義一個父類,再在父類中定義必須要實作的功能。這樣子類在繼承父類時,就可以避免忘記必須要實作的功能函數了,它是用來規範子類的方法。實際上基類不用進行執行個體化操作,因為它完全沒有意義。

該部分的代碼塊為:

import abc
class School(metaclass=abc.ABCMeta):
    "學校的基類"
    @abc.abstractclassmethod
    def course(self):
        "學校開課的方法"
        pass
    @abc.abstractclassmethod
    def activity(self):
        "校園活動的方法"
        pass

class Kindergarten(School):
    "幼稚園的類"
    def __init__(self,name,addr,number):
        self.name =name
        self.addr =addr
        self.number =number
    def course(self):
        "課程函數"
        print("%s開設了字母、識數等課程"%self.name)
    def activity(self):
        "比賽活動"
        print("%s參加了兒童舞蹈比賽"%self.number)

class College(School):
    "幼稚園的類"
    def __init__(self, name, addr, number):
        self.name = name
        self.addr = addr
        self.number = number
    # def course(self):
    #     "課程函數"
    #     print("%s開設了微積分、高等數學等課程" % self.name)
    def activity(self):
        "比賽活動"
        print("%s參加了大學口語比賽"%self.name)
    def  volunta(self):
        "義務活動"
        print("這是志願者活動")

kindergarten =Kindergarten("文瀾幼稚園","杭州市拱墅區",300)
kindergarten.activity()

college =College("浙江大學","杭州市上城區",65300)
college.course()           

複制