走入計算機的第二十九天(繼承與派生)
一 什麽是繼承
繼承是一種創建新類的方式,在python中,新建的類可以繼承一個或多個父類,父類又可稱為基類或超類,新建的類稱為派生類或子類
單繼承:就相當於子類繼承了一個父類。
多繼承:就相當於子類繼承了多個父類。
python中類的繼承分為:單繼承和多繼承
1 class ParentClass1: #定義父類 2 pass 3 4 class ParentClass2: #定義父類 5 pass 6 7 class SubClass1(ParentClass1): #單繼承,基類是ParentClass1,派生類是SubClass 8 pass9 10 class SubClass2(ParentClass1,ParentClass2): #python支持多繼承,用逗號分隔開多個繼承的類 11 pass
查看繼承
查看類繼承了哪個父類的方式:子類名.__bases__
>>> SubClass1.__bases__ #__base__只查看從左到右繼承的第一個子類,__bases__則是查看所有繼承的父類 (<class ‘__main__.ParentClass1‘>,) >>> SubClass2.__bases__ (<class ‘__main__.ParentClass1‘>, <class ‘__main__.ParentClass2‘>)
提示:如果沒有指定基類,python的類會默認繼承object類,object是所有python類的基類,它提供了一些常見方法(如__str__)的實現。
1 >>> ParentClass1.__bases__ 2 (<class ‘object‘>,) 3 >>> ParentClass2.__bases__ 4 (<class ‘object‘>,)
在python3中,所有類默認繼承object(在python3當中所有類都是新式類)
但凡是繼承了object類的子類以及該子類的子類,都稱為新式類
沒有繼承object類的子類成為經典類(在python2 中,沒有繼承object類,以及它的子類,都是經典類。)
二 繼承與抽象(先抽象再繼承)
抽象即抽取類似或者說比較像的部分。
解決代碼重用的問題,減少代碼冗余
繼承是一種什麽‘是’什麽的關系
抽象分成兩個層次:
1.將奧巴馬和梅西這倆對象比較像的部分抽取成類;
2.將人,豬,狗這三個類比較像的部分抽取成父類。
抽象最主要的作用是劃分類別(可以隔離關註點,降低復雜度)
繼承:是基於抽象的結果,通過編程語言去實現它,肯定是先經歷抽象這個過程,才能通過繼承的方式去表達出抽象的結構。
抽象只是分析和設計的過程中,一個動作或者說一種技巧,通過抽象可以得到類
三 組合與重用性
軟件重用的重要方式除了繼承之外還有另外一種方式,即:組合
組合指的是,在一個類中以另外一個類的對象作為數據屬性,稱為類的組合
組合:什麽有什麽的關系,也是為了減少重復代碼
其實早在3.5小節中我們就體會了組合的用法,比如一個英雄有一個裝備
1 >>> class Equip: #武器裝備類 2 ... def fire(self): 3 ... print(‘release Fire skill‘) 4 ... 5 >>> class Riven: #英雄Riven的類,一個英雄需要有裝備,因而需要組合Equip類 6 ... camp=‘Noxus‘ 7 ... def __init__(self,nickname): 8 ... self.nickname=nickname 9 ... self.equip=Equip() #用Equip類產生一個裝備,賦值給實例的equip屬性 10 ... 11 >>> r1=Riven(‘銳雯雯‘) 12 >>> r1.equip.fire() #可以使用組合的類產生的對象所持有的方法 13 release Fire skill
組合與繼承都是有效地利用已有類的資源的重要方式。但是二者的概念和使用場景皆不同,
1.繼承的方式
通過繼承建立了派生類與基類之間的關系,它是一種‘是‘的關系,比如白馬是馬,人是動物。
當類之間有很多相同的功能,提取這些共同的功能做成基類,用繼承比較好,比如教授是老師
1 >>> class Teacher: 2 ... def __init__(self,name,gender): 3 ... self.name=name 4 ... self.gender=gender 5 ... def teach(self): 6 ... print(‘teaching‘) 7 ... 8 >>> 9 >>> class Professor(Teacher): 10 ... pass 11 ... 12 >>> p1=Professor(‘egon‘,‘male‘) 13 >>> p1.teach() 14 teaching
用組合的方式建立了類與組合的類之間的關系,它是一種‘有’的關系,比如教授有生日,教授教python課程
1 class People: 2 # def __init__(self,name,age,year,mon,day): 3 # self.name=name 4 # self.age=age 5 # self.birth=Date(year,mon,day) 6 # def walk(self): 7 # print(‘%s is walking‘ %self.name) 8 # class Date: 9 # def __init__(self,year,mon,day): 10 # self.year=year 11 # self.mon=mon 12 # self.day=day 13 # def tell_birth(self): 14 # print("出生於<%s>年 <%s>月 <%s>日"%(self.year,self.mon,self.day)) 15 # 16 # class Student(People): 17 # def __init__(self,name,age,year,mon,day,group): 18 # People.__init__(self,name,age,year,mon,day) 19 # self.group=group 20 # def study(self): 21 # print(‘%s is studying‘ %self.name) 22 # 23 # class Teacher(People): 24 # def __init__(self,name,age,year,mon,day,level,salary): 25 # People.__init__(self,name,age,year,mon,day) 26 # self.level=level 27 # self.salsry=salary 28 # def teach(self): 29 # print(‘%s is teaching‘ %self.name) 30 # 31 # t=Teacher(‘egon‘,18,1990,2,33) 32 # print(t.name,t.age) 33 # print(t.birth) 34 # print(t.birth.year) 35 # print(t.birth.mon) 36 # print(t.birth.day) 37 # 38 # 39 # 40 # t=Student(‘fang‘,18,1990,2,25) 41 # print(t.name,t.age) 42 # print(t.birth) 43 # print(t.birth.year) 44 # print(t.birth.mon) 45 # print(t.birth.day)View Code
當類之間有顯著不同,並且較小的類是較大的類所需要的組件時,用組合比較好
四 接口與歸一化設計
什麽是接口?接口只是定義了一些方法,而沒有去實現,多用於程序設計時,只是設計需要有什麽樣的功能,但是並沒有實現任何功能,這些功能需要被另一個類(B)繼承後,由 類B去實現其中的某個功能或全部功能。,
繼承有兩種用途:
1 繼承基類的方法,並且做出自己的改變或者擴展(代碼重用)
2 聲明某個子類兼容於某基類,定義一個接口類Interface,接口類中定義了一些接口名(就是函數名)且並未實現接口的功能,子類繼承接口類,並且實現接口中的功能
1 # class Interface: #定義接口Interface類來模仿接口的概念,python中壓根就沒有interface關鍵字 2 # def read(self): #定接口函數read 3 # pass 4 # def write(self): #定接口函數write 5 # pass 6 # class Txt(Interface): #文本,具體實現read和write 7 # def read(self): 8 # print(‘文本數據的讀取方法‘) 9 # def write(self): 10 # print(‘文本數據的寫入方法‘) 11 # class Sata(Interface): #硬盤,具體實現read和write 12 # def read(self): 13 # print(‘硬盤數據的讀取方法‘) 14 # def write(self): 15 # print(‘硬盤數據的寫入方法‘) 16 # class Process(Interface): 17 # def read(self): 18 # print(‘進程數據的讀取方法‘) 19 # def write(self): 20 # print(‘進程數據的寫入方法‘) 21 # 22 # 23 # txt=Txt() 24 # disk=Sata() 25 # process=Process() 26 # 27 # 28 # txt.read() 29 # disk.read() 30 # process.read()View Code
實踐中,繼承的第一種含義意義並不很大,甚至常常是有害的。因為它使得子類與基類出現強耦合。
繼承的第二種含義非常重要。它又叫“接口繼承”。
接口繼承實質上是要求“做出一個良好的抽象,這個抽象規定了一個兼容接口,使得外部調用者無需關心具體細節,可一視同仁的處理實現了特定接口的所有對象”——這在程序設計上,叫做歸一化。
歸一化使得高層的外部使用者可以不加區分的處理所有接口兼容的對象集合——就好象linux的泛文件概念一樣,所有東西都可以當文件處理,不必關心它是內存、磁盤、網絡還是屏幕(當然,對底層設計者,當然也可以區分出“字符設備”和“塊設備”,然後做出針對性的設計:細致到什麽程度,視需求而定)。
在python中根本就沒有一個叫做interface的關鍵字,上面的代碼只是看起來像接口,其實並沒有起到接口的作用,子類完全可以不用去實現接口 ,如果非要去模仿接口的概念,可以借助第三方模塊:
為何要用接口
接口提取了一群類共同的函數,可以把接口當做一個函數的集合。
然後讓子類去實現接口中的函數。
這麽做的意義在於歸一化,什麽叫歸一化,就是只要是基於同一個接口實現的類,那麽所有的這些類產生的對象在使用時,從用法上來說都一樣。
歸一化,讓使用者無需關心對象的類是什麽,只需要的知道這些對象都具備某些功能就可以了,這極大地降低了使用者的使用難度。
比如:我們定義一個動物接口,接口裏定義了有跑、吃、呼吸等接口函數,這樣老鼠的類去實現了該接口,松鼠的類也去實現了該接口,由二者分別產生一只老鼠和一只松鼠送到你面前,即便是你分別不到底哪只是什麽鼠你肯定知道他倆都會跑,都會吃,都能呼吸。
再比如:我們有一個汽車接口,裏面定義了汽車所有的功能,然後由本田汽車的類,奧迪汽車的類,大眾汽車的類,他們都實現了汽車接口,這樣就好辦了,大家只需要學會了怎麽開汽車,那麽無論是本田,還是奧迪,還是大眾我們都會開了,開的時候根本無需關心我開的是哪一類車,操作手法(函數調用)都一樣
走入計算機的第二十九天(繼承與派生)