Python_類的繼承
- 2020 年 1 月 6 日
- 筆記
1.類的繼承關係和生活中父親、兒子、孫子之間的關係一樣,Python中若A類繼承B類,則A類稱之為子類,B類稱之為父類(也稱為基類)。 2.類的繼承方式分為:單繼承、多繼承兩種; 類的單繼承是指,A類只繼承一個父類B,如下圖所示:

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

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

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

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

總結: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會計算出一個方法解析順序(MRO)列表,這個MRO列表就是一個簡單的所有基類的線性順序列表。 為了實現繼承,python會在MRO列表上從左到右查找基類,直到找到第一個匹配這個屬性的類為止。而這個MRO列表的構造是通過一個C3線性化演算法來實現的。這個演算法實際上就是合併所有父類的,MRO列表並遵循如下三條準則: ① 子類會由於父類被檢查; ② 多個父類會根據他們在列表中的順序被檢查; ③ 如果對於下一個類存在兩個合法的選擇,應選擇第一個父類;
為此我們可以直接使用子類的mro或者mro方法來查詢它自身的繼承順序,如下圖所示:

4.介面繼承 從上面例子中我們可以看出,類的繼承有2種含義,一是:子類繼承基類的方法,並作出自己的擴展或改變(基類程式碼的重用);二是:聲明某個子類兼容於某基類,父類定義一個介面類,子類繼承介面類,並且實現介面類中定義的方法。 實踐中,繼承的第一種含義常常不建議使用,它是利小於弊,會出現程式碼的強耦合,不利於後面的維護和擴展;但介面的第二中含義十分重要,又稱之為』介面繼承』。 介面繼承實質上是:「要求做出一個抽象,這個抽象規定了一個兼容介面,使得外部調用者無需了解具體細節,可一視同仁的處理實現了特定介面的所有對象。」這種在程式設計上稱之為歸一化。 例如:定義一個學校的基類,學校有不能的課程、學校有不同的活動,但是幼兒園的課程只是簡單的字母識別、數數等,而大學的課程包含了微積分、專業課程等。如下圖所示,通過介面的繼承來實現上面的要求:

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

所以,介面繼承就是在基類中定義子類要實現的方法名稱(使用@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()