1. 程式人生 > >Python全棧之路系列----之-----面向對象4接口與抽象,多繼承與多態)

Python全棧之路系列----之-----面向對象4接口與抽象,多繼承與多態)

統一 dog blog 水果 創建 設計 概念 fly 支付

接口類與抽像類

在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()
新式類
  1. 經典類:首先去A類中查找,如果A類中沒有,則繼續去B類中找,如果B類中麽有,則繼續去D類中找,如果D類中麽有,則繼續去C類中找,如果還是未找到,則報錯
  2. 新式類:首先去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接口與抽象,多繼承與多態)