Python全棧之路系列----之-----面向對象4接口與抽象,多繼承與多態)
接口類與抽像類
在python中,並沒有接口類這種東西,即便不通過專門的模塊定義接口,我們也應該有一些基本的概念
編程思想 歸一化設計:
1.接口類 不實現具體的方法,並且可以多繼承
2.抽象類 可以做一些基礎實現,並且不推薦多繼承
編程的幾類原則:
開放封閉原則:對擴展示開放的,對修改是封閉的
依賴倒置原則:高層模塊不應該依賴低層模塊,二者都應該依賴其抽象;抽象不應該應該依賴細節;細節應該依賴抽象。換言之,要針對接口編程,而不是針對實現編程
接口隔離原則:使用多個專門的接口,而不使用單一的總接口。即客戶端不應該依賴那些不需要的接口。
接口類
接口類概念小述:
接口提取了一群類共同的函數,可以把接口當做一個函數的集合。 然後讓子類去實現接口中的函數。 這麽做的意義在於歸一化,什麽叫歸一化,就是只要是基於同一個接口實現的類,那麽所有的這些類產生的對象在使用時,從用法上來說都一樣。 歸一化,讓使用者無需關心對象的類是什麽,只需要的知道這些對象都具備某些功能就可以了,這極大地降低了使用者的使用難度。 比如:我們定義一個動物接口,接口裏定義了有跑、吃、呼吸等接口函數,這樣老鼠的類去實現了該接口,松鼠的類也去實現了該接口,由二者分別產生一只老鼠和一只松鼠送到你面前,即便是你分別不到底哪只是什麽鼠你肯定知道他倆都會跑,都會吃,都能呼吸。 再比如:我們有一個汽車接口,裏面定義了汽車所有的功能,然後由本田汽車的類,奧迪汽車的類,大眾汽車的類,他們都實現了汽車接口,這樣就好辦了,大家只需要學會了怎麽開汽車,那麽無論是本田,還是奧迪,還是大眾我們都會開了,開的時候根本無需關心我開的是哪一類車,操作手法(函數調用)都一樣 為何要用接口概念小述
接口類:是一種推崇設計模式,增強代碼可讀性的一種類,他能有效的約束子類代碼的編寫規範
from abc import abstractmethod,ABCMeta #導入模塊
# class Payment(metaclass=ABCMeta): #抽象類/接口類:子類的規範
# @abstractmethod #裝飾器裝飾並約束
# def payment(self,money):pass #支付函數,總體負責支付 對應支付的對象和要支付的金額
class Applepay():
def payment(self,money):
print(‘apple 支付了 %d元‘%money)
class Alipay():
def payment(self,money):
print(‘支付寶 支付了 %d元‘ % money)
class Wechatpay():
def payment(self,money):
print(‘微信 支付了 %d元‘ % money)
def payment(pay_obj,money): #歸一化設計
pay_obj.payment(money)
apple = Applepay()
ali = Alipay()
wechat = Wechatpay()
payment(wechat,100)
payment(apple,100)
payment(ali,100)
借用abc模塊來實現接口
from abc import ABCMeta,abstractmethod
class Payment(metaclass=ABCMeta):
@abstractmethod
def pay(self,money):
pass
class Wechatpay(Payment):
def fuqian(self,money):
print(‘微信支付了%s元‘%money)
p = Wechatpay() #不調就報錯了
主動拋異常
接口初成:手動報異常:NotImplementedError來解決開發中遇到的問題
class Payment: def pay(self): raise NotImplementedError class Wechatpay(Payment): def fuqian(self,money): print(‘微信支付了%s元‘%money) p = Wechatpay() #這裏不報錯 pay(p,200) #這裏報錯了
抽象類
什麽是抽象類
抽象類的本質還是類,指的是一組類的相似性,包括數據屬性(如all_type)和函數屬性(如read、write),而接口只強調函數屬性的相似性。
抽象類是一個介於類和接口直接的一個概念,同時具備類和接口的部分特性,可以用來實現歸一化設計
與java一樣,python也有抽象類的概念但是同樣需要借助模塊實現,抽象類是一個特殊的類,它的特殊之處在於只能被繼承,不能被實例化
為什麽要有抽象類
如果說類是從一堆對象中抽取相同的內容而來的,那麽抽象類就是從一堆類中抽取相同的內容而來的,內容包括數據屬性和函數屬性。
比如我們有香蕉的類,有蘋果的類,有桃子的類,從這些類抽取相同的內容就是水果這個抽象的類,你吃水果時,要麽是吃一個具體的香蕉,要麽是吃一個具體的桃子。。。。。。你永遠無法吃到一個叫做水果的東西。
從設計角度去看,如果類是從現實對象抽象而來的,那麽抽象類就是基於類抽象而來的。
從實現角度來看,抽象類與普通類的不同之處在於:抽象類中只能有抽象方法(沒有實現功能),該類不能被實例化,只能被繼承,且子類必須實現抽象方法。這一點與接口有點類似,但其實是不同的,即將揭曉答案
在python中實現抽象類
#一切皆文件 import abc #利用abc模塊實現抽象類 class All_file(metaclass=abc.ABCMeta): all_type=‘file‘ @abc.abstractmethod #定義抽象方法,無需實現功能 def read(self): ‘子類必須定義讀功能‘ pass @abc.abstractmethod #定義抽象方法,無需實現功能 def write(self): ‘子類必須定義寫功能‘ pass # class Txt(All_file): # pass # # t1=Txt() #報錯,子類沒有定義抽象方法 class Txt(All_file): #子類繼承抽象類,但是必須定義read和write方法 def read(self): print(‘文本數據的讀取方法‘) def write(self): print(‘文本數據的讀取方法‘) class Sata(All_file): #子類繼承抽象類,但是必須定義read和write方法 def read(self): print(‘硬盤數據的讀取方法‘) def write(self): print(‘硬盤數據的讀取方法‘) class Process(All_file): #子類繼承抽象類,但是必須定義read和write方法 def read(self): print(‘進程數據的讀取方法‘) def write(self): print(‘進程數據的讀取方法‘) wenbenwenjian=Txt() yingpanwenjian=Sata() jinchengwenjian=Process() #這樣大家都是被歸一化了,也就是一切皆文件的思想 wenbenwenjian.read() yingpanwenjian.write() jinchengwenjian.read() print(wenbenwenjian.all_type) print(yingpanwenjian.all_type) print(jinchengwenjian.all_type抽象類
繼承
多繼承問題
在繼承抽象類的過程中,我們應該盡量避免多繼承;而在繼承接口的時候,我們反而鼓勵你來多繼承接口
方法的實現
在抽象類中,我們可以對一些抽象方法做出基礎實現;而在接口類中,任何方法都只是一種規範,具體的功能需要子類實現
Python的類可以繼承多個類,Java和C#中則只能繼承一個類
Python的類如果繼承了多個類,那麽其尋找方法的方式有兩種,分別是:深度優先
- 當類是經典類時,多繼承情況下,會按照深度優先方式查找
- 當類是新式類時,多繼承情況下,會按照廣度優先方式查找
- 經典類和新式類,從字面上可以看出一個老一個新,新的必然包含了跟多的功能,也是之後推薦的寫法,從寫法上區分的話,如果 當前類或者父類繼承了object類,那麽該類便是新式類,否則便是經典類。
代碼理解
1.多繼承
#一般情況下不用 #接口 from abc import abstractmethod,ABCMeta class Fly_Animal(metaclass=ABCMeta): #接口類 @abstractmethod def fly(self):pass class Swim_Animal: def swim(self):pass class Walk_Animal: def walk(self):pass class Tiger(Walk_Animal,Swim_Animal): def walk(self): print(‘tiger is walking‘) class Frog(Walk_Animal,Swim_Animal): def walk(self): pass def swim(self): pass class Swan(Walk_Animal,Fly_Animal,Swim_Animal): def fly(self): print(‘‘) def swim(self): pass def walk(self): pass多繼承
2.經典類多繼承
class D: def bar(self): print ‘D.bar‘ class C(D): def bar(self): print ‘C.bar‘ class B(D): def bar(self): print ‘B.bar‘ class A(B, C): def bar(self): print ‘A.bar‘ a = A() # 執行bar方法時 # 首先去A類中查找,如果A類中沒有,則繼續去B類中找,如果B類中麽有,則繼續去D類中找,如果D類中麽有,則繼續去C類中找,如果還是未找到,則報錯 # 所以,查找順序:A --> B --> D --> C # 在上述查找bar方法的過程中,一旦找到,則尋找過程立即中斷,便不會再繼續找了 a.bar()經典類
3.新式類多繼承
#object 所有類的基類 #class A(object): #新式類 pass class D(object): def bar(self): print ‘D.bar‘ class C(D): def bar(self): print ‘C.bar‘ class B(D): def bar(self): print ‘B.bar‘ class A(B, C): def bar(self): print ‘A.bar‘ a = A() # 執行bar方法時 # 首先去A類中查找,如果A類中沒有,則繼續去B類中找,如果B類中麽有,則繼續去C類中找,如果C類中麽有,則繼續去D類中找,如果還是未找到,則報錯 # 所以,查找順序:A --> B --> C --> D # 在上述查找bar方法的過程中,一旦找到,則尋找過程立即中斷,便不會再繼續找了 a.bar()新式類
- 經典類:首先去A類中查找,如果A類中沒有,則繼續去B類中找,如果B類中麽有,則繼續去D類中找,如果D類中麽有,則繼續去C類中找,如果還是未找到,則報錯
- 新式類:首先去A類中查找,如果A類中沒有,則繼續去B類中找,如果B類中麽有,則繼續去C類中找,如果C類中麽有,則繼續去D類中找,如果還是未找到,則報錯
**註意:88在上述查找過程中,一旦找到,則尋找過程立即中斷,便不會再繼續找
新式類中查看多繼承順序的關鍵字:mro()
a = A()
a.test()
print(A.mro())
小結
class A(object): def test(self): print(‘from A‘) class B(A): def test(self): print(‘from B‘) class C(A): def test(self): print(‘from C‘) class D(B): def test(self): print(‘from D‘) class E(C): def test(self): print(‘from E‘) class F(D,E): # def test(self): # print(‘from F‘) pass f1=F() f1.test() print(F.__mro__) #只有新式才有這個屬性可以查看線性列表,經典類沒有這個屬性 #新式類繼承順序:F->D->B->E->C->A #經典類繼承順序:F->D->B->A->E->C #python3中統一都是新式類 #pyhon2中才分新式類與經典類 繼承原理 python到底是如何實現繼承的,對於你定義的每一個類,python會計算出一個方法解析順序(MRO)列表,這個MRO列表就是一個簡單的所有基類的線性順序列表,例如 >>> F.mro() #等同於F.__mro__ [<class ‘__main__.F‘>, <class ‘__main__.D‘>, <class ‘__main__.B‘>, <class ‘__main__.E‘>, <class ‘__main__.C‘>, <class ‘__main__.A‘>, <class ‘object‘>] 為了實現繼承,python會在MRO列表上從左到右開始查找基類,直到找到第一個匹配這個屬性的類為止。 而這個MRO列表的構造是通過一個C3線性化算法來實現的。我們不去深究這個算法的數學原理,它實際上就是合並所有父類的MRO列表並遵循如下三條準則: 1.子類會先於父類被檢查 2.多個父類會根據它們在列表中的順序被檢查 3.如果對下一個類存在兩個合法的選擇,選擇第一個父類 繼承小結 繼承的作用 減少代碼的重用 提高代碼可讀性 規範編程模式 幾個名詞 抽象:抽象即抽取類似或者說比較像的部分。是一個從具題到抽象的過程。 繼承:子類繼承了父類的方法和屬性 派生:子類在父類方法和屬性的基礎上產生了新的方法和屬性 抽象類與接口類 復制代碼 1.多繼承問題 在繼承抽象類的過程中,我們應該盡量避免多繼承; 而在繼承接口的時候,我們反而鼓勵你來多繼承接口 2.方法的實現 在抽象類中,我們可以對一些抽象方法做出基礎實現; 而在接口類中,任何方法都只是一種規範,具體的功能需要子類實現 鉆石繼承 新式類:廣度優先 經典類:深度優先小結
多態
多態指的是一類事物有多種形態
動物有多種形態:人,狗,豬
import abc class Animal(metaclass=abc.ABCMeta): #同一類事物:動物 @abc.abstractmethod def talk(self): pass class People(Animal): #動物的形態之一:人 def talk(self): print(‘say hello‘) class Dog(Animal): #動物的形態之二:狗 def talk(self): print(‘say wangwang‘) class Pig(Animal): #動物的形態之三:豬 def talk(self): print(‘say aoao‘)多態
文件有多種形態:文本文件,可執行文件
多態多態性
一 什麽是多態動態綁定(在繼承的背景下使用時,有時也稱為多態性)
多態性是指在不考慮實例類型的情況下使用實例
在面向對象方法中一般是這樣表述多態性:
向不同的對象發送同一條消息(!!!obj.func():是調用了obj的方法func,又稱為向obj發送了一條消息func),不同的對象在接收時會產生不同的行為(即方法)。
也就是說,每個對象可以用自己的方式去響應共同的消息。所謂消息,就是調用函數,不同的行為就是指不同的實現,即執行不同的函數。
比如:老師.下課鈴響了(),學生.下課鈴響了(),老師執行的是下班操作,學生執行的是放學操作,雖然二者消息一樣,但是執行的效果不同
多態性
peo=People() dog=Dog() pig=Pig() #peo、dog、pig都是動物,只要是動物肯定有talk方法 #於是我們可以不用考慮它們三者的具體是什麽類型,而直接使用 peo.talk() dog.talk() pig.talk() #更進一步,我們可以定義一個統一的接口來使用 def func(obj): obj.talk()多態
鴨子類型
Python崇尚鴨子類型,即‘如果看起來像、叫聲像而且走起路來像鴨子,那麽它就是鴨子’ python程序員通常根據這種行為來編寫程序。例如,如果想編寫現有對象的自定義版本,可以繼承該對象 也可以創建一個外觀和行為像,但與它無任何關系的全新對象,後者通常用於保存程序組件的松耦合度。 例1:利用標準庫中定義的各種‘與文件類似’的對象,盡管這些對象的工作方式像文件,但他們沒有繼承內置文件對象的方法 例2:序列類型有多種形態:字符串,列表,元組,但他們直接沒有直接的繼承關系 #二者都像鴨子,二者看起來都像文件,因而就可以當文件一樣去用 class TxtFile: def read(self): pass def write(self): pass class DiskFile: def read(self): pass def write(self): pass
Python全棧之路系列----之-----面向對象4接口與抽象,多繼承與多態)