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()
複制