基類使用私有資料_【進階Python】第七講:介面與抽象基類
點選藍字關注我
抽象基類(abstract base class,ABC),提到這個概念應該會馬上聯想到面向物件、繼承。作為繼承的一種,它擁有繼承中程式碼共享、提高程式碼的重用性等優點,此外它還擁有介面相關的一些特性,本文就來介紹一下Python抽象基類的使用。
—▼—
前言
抽象基類(abstract base class,ABC),提到這個概念應該會馬上聯想到面向物件、繼承。作為繼承的一種,它擁有繼承中程式碼共享、提高程式碼的重用性等優點。例如,下面示例,狗(Dog)和貓(Cat)都屬於動物(Animal),它們有很多類似的屬性和動作,我們可以在父類中實現這些方法,在子類中直接繼承或者過載父類中的方法,這樣減少了程式碼的重複性,提高了程式碼的共享能力。 作為繼承的一種,抽象基類有用繼承的上述這些優點,但是它與普通的繼承也有不同之處,classAnimal(object):defeat(self,kind):print("{}eatfood....".format(kind))classDog(Animal):passclassCat(Animal):passdog=Dog()cat=Cat()dog.eat("dog")cat.eat("cat")#輸出dogeatfood....cateatfood....
抽象基類不能例項化
子類需要實現基類指定的抽象方法
介面
介面(Interface)是物件公開方法的一種集合,在Java中通常以interface關鍵字來定義,介面雖然實現過程中和類相似,但是卻具有不同的概念。具體而言,類與介面主要有以下幾點不同之處:
類實現了物件的屬性和方法,而介面指定了使用該介面需要實現哪些方法
類可以例項化,而介面不可以被例項化
類中的方法可以是實現,介面中的方法都是抽象方法
抽象方法:抽象方法的概念是父類中只負責宣告該方法,但不具體實現這個方法,實現部分由繼承該類的子類負責實現。
如果覺得上述描述有點雲裡霧裡、對介面的概念依然不是非常清楚,不妨來試想一個場景:當你開發一個專案或者服務,你需要給上下游的元件提供介面,讓別人來呼叫你的程式介面(Application Programming Interface,API),上下游元件該怎麼樣才能達到想要的目的和你的元件無縫銜接?需要通過按照你介面中規定的抽象方法來實現,例如,你提供一個訪問網路請求的介面,你不會去實現host、username、password的註冊和傳送請求,這些需要呼叫的使用者去實現,你只需要規定:“呼叫者必須實現指定方法才能實現呼叫”即可。
抽象基類
雖然Python中抽象基類和介面概念非常相近,但是它們還是有一些不同之處,例如,
介面需要被實現的子類完成介面中指定的所有方法,而抽象基類不是,抽象基類則沒有這麼嚴格的要求
介面需要所有方法都是抽象方法,而抽象基類中有抽象方法,也有自己實現的方法
正是因為抽象基類和介面的不同之處使得介面之所以稱為介面、抽象基類之所以稱為抽象基類。
為什麼使用抽象基類?
前面鋪墊了這麼多,話說回來,為什麼需要抽象基類?
存在的即是合理的,抽象基類的存在自然有它的價值。當你學會一種程式語言的語法時,你可以輕鬆的完成一項功能的開發,但是如果希望把程式碼完成的更加優美高效,那麼就需要在設計模式等方面下一些功夫,抽象基類就是其中的一個選擇,抽象基類具有以下優點:
處理繼承問題方面更加規範、系統
明確呼叫之間的相互關係
使得繼承層次更加清晰
限定子類實現的方法
什麼是抽象基類?
前面已經介紹了很多有關介面的概念,抽象基類和介面有很多相似之處,例如需要包含抽象方法,不能被例項化,如果更加確切的定義抽象基類:必須包含一個抽象函式(純虛擬函式),它是一個不完整的類,它有已經被實現的方法,也有需要子類重寫的方法。
抽象基類使用場景
一項功能只有具有應用場景才能體現出它的價值,如果僅僅是為了看上去高逼格,那麼倒不如使用最簡單的條件、迴圈語句,沒必要花裡胡哨,讓程式碼變得難以維護、晦澀難懂。
抽象基類首先它具備普通繼承的功能,因此,在程式碼可以共用,或者需要獲取額外屬性的時候可以考慮使用抽象基類,例如,狗、貓、牛、羊這些動物有很多共有的屬性和方法,我們可以通過實現一個基類,讓每個特定的物件來繼承它,這樣不僅可以實現多型,還可以提高程式碼的複用能力。
當然,上述說的這些場景都偏重於普通繼承的優勢,而抽象基類的特別之處更加偏向於介面的特點,因此,它的使用場景和介面也有很多相通之處,例如我們開發一個系統,下面有若干個元件,每個元件都需要按照指定的規範來實現特定的方法,這時候我可以發揮抽象基類的限定功能的優勢。
下面就結合這個場景來介紹Python中抽象基類的實現方法。
Python抽象基類
場景介紹
假如我們現在實現了一個數據中臺的開發,我們對外提供一個介面讓不同元件通過這個介面進行訪問資料庫,來讀取資料,我們給資料介面主要有2個功能,
登入資料庫
讀取資料
執行SQL語句
可以想象,登入資料庫這個功能在不同元件之間可以共用,不同元件只需要提供host、user、passwd即可,至於讀取資料這是每個元件都必須單獨實現的,可以宣告為抽象方法,執行SQL語句也是每個子類需要實現的,可以宣告為抽象的靜態方法。
實現
Python標準庫中有一個模組abc可以實現抽象基類和抽象方法,它們的實現方式如下:
抽象基類:通過繼承abc模組中的ABC類來實現抽象基類。
抽象方法:通過裝飾器的方法來呼叫abc模組中abstractmethod方法來註解抽象基類的方法。
abstractmethod註解除了可以實現抽象方法外,還可以註解類方法(@classmethod)、靜態方法(@staticmethod)、屬性(@property)。
下面就先實現抽象基類,
fromabcimportABCfromabcimportabstractmethodclassDatabase(ABC):defregister(self,host,user,password):print("Host:{}".format(host))print("User:{}".format(user))print("Password:{}".format(password))print("RegisterSuccess!")@abstractmethoddefquery(self,*args):"""
傳入查詢資料的SQL語句並執行
"""@staticmethod@abstractmethoddefexecute(sql_string):"""
執行SQL語句
"""
從抽象基類Database的實現可以看出,它共包含3個方法,其中register是每個子類都需要的,直接實現在抽象基類裡,是一個普通的類方法。query和execute只是在基類中進行類宣告,給出了描述,但並沒有實現,它限定了繼承Database的子類必須實現這兩個方法。
下面就來實現兩個元件(子類),
classComponent1(Database):def__init__(self,host,user,password):self.register(host,user,password)@staticmethoddefexecute(sql_string):print(sql_string)defquery(self,*args):sql_string="SELECTIDFROMdb_name"self.execute(sql_string)classComponent2(Database):def__init__(self,host,user,password):self.register(host,user,password)@staticmethoddefexecute(sql_string):print(sql_string)defquery(self,*args):sql_string="SELECTNAMEFROMdb_name"self.execute(sql_string)comp1=Component1("00.00.00.00","abc","000000")comp2=Component2("11.11.11.11","ABC","111111")comp1.query()comp2.query()#輸出結果Host:00.00.00.00User:abcPassword:000000RegisterSuccess!Host:11.11.11.11User:ABCPassword:111111RegisterSuccess!SELECTIDFROMdb_nameSELECTNAMEFROMdb_name
上述是通過Python標準庫中abc模組實現了抽象基類,其實在Python中collections中也實現了抽象基類,numbers中也定義了有關數字物件的抽象基類。可見,抽象基類在Python中佔據著至關重要的地位。
完整程式碼
本文所涉及的完整程式碼可以檢視github專案advance-python,也可以直接訪問下方連結,
https://github.com/Jackpopc/advance-python/blob/master/5-abstract%20.ipynb
▲
END
有趣的靈魂在等你長按掃碼可關注
相關文章 【進階Python】第一講: 開篇 【進階Python】第二講: 裝飾器 【進階Python】第三講: 類的特殊方法(上篇) 【進階Python】第四講: 類的特殊方法(下篇) 【進階Python】第五講: 迭代器與生成器 【進階Python】第六講: 單例模式的妙用文章好看就點這裡