1. 多態(tài)
什么是多態(tài)
-- 多態(tài),指的是一種事務(wù)具有多種形態(tài)们豌;
-- python是一種動(dòng)態(tài)語(yǔ)言蝗拿,默認(rèn)支持多態(tài),同一個(gè)方法 調(diào)用 不同的類(lèi)對(duì)象 补履,執(zhí)行的 結(jié)果各不相同添坊;多態(tài)實(shí)現(xiàn)
-- 繼承:不同子類(lèi) 繼承 同一父類(lèi);
-- 重寫(xiě):子類(lèi)重寫(xiě) 同一個(gè)方法箫锤,保證執(zhí)行結(jié)果各不相同贬蛙;示例
-- 有如下代碼:
>>> class Animals():
... def talk(self):
... print("Animal talk")
...
>>>
>>> class People(Animals): # 繼承 Animals 類(lèi)
... def talk(self):
... print('People speak language')
...
>>>
>>> class Cat(Animals): # 繼承 Animals 類(lèi)
... def talk(self):
... print('Cat say miaomiao')
...
>>>
>>> cat = Cat()
>>> peo = People()
>>>
>>> cat.talk() # 調(diào)用 talk 方法
Cat say miaomiao
>>> peo.talk() # 調(diào)用 talk 方法
People speak language
- 如上所示:
-- cat 和 peo 兩個(gè)對(duì)象調(diào)用同一個(gè) talk() 方法;
-- 最后得到兩種不同的結(jié)果谚攒; - 多態(tài)的優(yōu)點(diǎn):
-- 多態(tài)可以增加代碼的靈活度阳准;
-- 是調(diào)用方法的技巧,不會(huì)影響到類(lèi)的內(nèi)部設(shè)計(jì)馏臭;
-- 多態(tài)可以看做 接口函數(shù)的重用野蝇,同一種接口方法 通過(guò) 接收不同的類(lèi) 對(duì)象,從而實(shí)現(xiàn)不同的功能括儒; - 多態(tài)使用場(chǎng)景:
-- 方法參數(shù)接收同一父類(lèi)的不同子類(lèi)對(duì)象浪耘。
2. 鴨子模型
什么是鴨子模型
-- 當(dāng)看到一只鳥(niǎo)走起來(lái)像鴨子,游泳起來(lái)也像鴨子塑崖,叫起來(lái)也像鴨子七冲,那么這只鳥(niǎo)就可以被稱(chēng)為鴨子;
-- 鴨子模型和多態(tài)一樣规婆,都是接受不同的類(lèi)對(duì)象澜躺,并調(diào)用相同的方法(即:鴨子的 游泳 和 叫 方法)蝉稳;
-- 對(duì)于一個(gè)鴨子模型來(lái)說(shuō),我們并 不關(guān)心接收的類(lèi)對(duì)象是否真的是鴨子類(lèi)掘鄙,只關(guān)心這個(gè)類(lèi)是如何被使用的耘戚;
-- 注意:如果這些需要被調(diào)用的方法不存在,那么將引發(fā)一個(gè)運(yùn)行時(shí)錯(cuò)誤操漠。示例
-- 有如下代碼:
>>> class Duck:
... def quack(self):
... print("duck quack")
...
>>>
>>> class Bird: # Bird 類(lèi)與 Duck 類(lèi)無(wú)繼承關(guān)系
... def quack(self):
... print("bird quack")
...
>>>
>>> class Dog: # Dog類(lèi)與 Duck 類(lèi)無(wú)繼承關(guān)系
... def quack(self):
... print("dog quack")
...
>>>
>>> def animal_quack(animal): # animal_quack 方法可以調(diào)用任何對(duì)象的 quack() 方法收津,不關(guān)心對(duì)象是誰(shuí)
... animal.quack()
...
>>>
>>> duck = Duck()
>>> bird = Bird() # bird 實(shí)例與 duck 實(shí)例無(wú)任何關(guān)系
>>> dog = Dog() # dog 實(shí)例與 duck 實(shí)例無(wú)任何關(guān)系
>>>
>>> for animal in [duck, bird, dog]:
... animal_quack(animal)
...
duck quack
bird quack
dog quack
-- 如上所示:
-- duck、bird浊伙、dog 分別來(lái)自三個(gè)不同的類(lèi)撞秋,而且類(lèi)之間是 沒(méi)有繼承關(guān)系 的;
-- duck嚣鄙、bird吻贿、dog 調(diào)用 animal_quack 方法,得到三種不同的結(jié)果哑子,符合多態(tài)的特征舅列;
- 鴨子模型的優(yōu)點(diǎn):
-- 鴨子模型不關(guān)關(guān)心類(lèi)對(duì)象是什么,不需要類(lèi)之間具有繼承關(guān)系卧蜓;
-- 鴨子模型讓代碼比多態(tài)更加靈活度帐要; - 多態(tài)使用場(chǎng)景:
-- 鴨子模型中,接收不同的類(lèi)將會(huì)產(chǎn)生不同的行為弥奸,而無(wú)須明確知道這個(gè)類(lèi)實(shí)際上是什么榨惠,這是多態(tài)的重要應(yīng)用場(chǎng)景;
-- 實(shí)際生產(chǎn)環(huán)境中其爵,主要用于 接口開(kāi)發(fā),即用同一個(gè)函數(shù)接收不同的類(lèi)對(duì)象伸蚯,從而實(shí)現(xiàn)不同的功能摩渺,而且無(wú)需關(guān)注對(duì)象之間的繼承關(guān)系;
3. 抽象基類(lèi)
- 什么是抽象基類(lèi)
-- 抽象基類(lèi)剂邮,這個(gè)詞可能聽(tīng)著比較"深?yuàn)W"摇幻,其實(shí) 抽象 就是 假 的意思,基類(lèi) 就是 父類(lèi)挥萌,抽象基類(lèi) 就是 假父類(lèi)绰姻;
-- 具體來(lái)說(shuō),由 abc.ABCMeta 這個(gè)元類(lèi)實(shí)現(xiàn)的類(lèi)引瀑,就是抽象基類(lèi)狂芋; - 示例:
-- 如下代碼中的 AbstractClass 類(lèi)繼承自 abc.ABCMeta,AbstractClass 就是抽象基類(lèi)憨栽;
class AbstractClass(metaclass=abc.ABCMeta):
pass
- 抽象基類(lèi)的作用
-- 判斷是否為某個(gè)對(duì)象的實(shí)例
>>> class MyList(object):
... def __init__(self, my_list):
... self.my_list= my_list
... def __len__(self):
... return len(self.my_list)
...
>>>
>>> class NewList(MyList): # NewList 繼承自 MyList
... pass
...
>>> ml = MyList(["a", "b", "c"])
>>>
>>> from collections.abc import Sized, Iterable
>>>
>>> print(isinstance(ml, Sized))
True # 返回 True帜矾,因?yàn)檫@里會(huì)檢查實(shí)例對(duì)象中有沒(méi)有__len__方法翼虫,有即輸出True
>>> nl = NewList([1, 2, 3])
>>> print(isinstance(nl, MyList))
True # 返回 True,因?yàn)?nl 實(shí)例化的類(lèi) NewList 同時(shí)也是 MyList 的子類(lèi)
-- 強(qiáng)制要求父類(lèi)被子類(lèi)繼承屡萤,并在子類(lèi)實(shí)現(xiàn)某個(gè)方法珍剑,否則子類(lèi)初始化時(shí)就會(huì)報(bào)錯(cuò);
>>> from abc import ABCMeta,abstractmethod
>>>
>>>
>>> class Source(metaclass=ABCMeta): # 創(chuàng)建抽象基類(lèi) Source
... @abstractmethod # 表示裝飾的方法必須被子類(lèi)所實(shí)現(xiàn)死陆,否則會(huì)報(bào)錯(cuò)
... def get(self,key):
... pass
...
>>>
>>> class Mysource(Source): # 子類(lèi) Mysource 繼承自 抽象基類(lèi) Source
... def get(self,key): # 實(shí)現(xiàn) get 方法招拙,這個(gè)方法是 抽象基類(lèi) Source 強(qiáng)制要求實(shí)現(xiàn)的
... pass
...
>>>
>>> class Mysource1(Source): # 子類(lèi) Mysource1 沒(méi)有實(shí)現(xiàn) 抽象基類(lèi) Source 強(qiáng)制要求實(shí)現(xiàn)的 get 方法
... pass
...
>>> test = Source() # test 直接實(shí)例化 Source 父類(lèi)
Traceback (most recent call last): # 此處報(bào)錯(cuò),因?yàn)槌橄箢?lèi)無(wú)法實(shí)現(xiàn)實(shí)例化
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Source with abstract methods get
>>>
>>> test = Mysource() # 此處實(shí)例化 Mysource措译,未報(bào)錯(cuò)
>>>
>>> test = Mysource1()
Traceback (most recent call last): # 報(bào)錯(cuò)别凤,繼承類(lèi)必須實(shí)現(xiàn)抽象類(lèi)的方法
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Mysource1 with abstract methods get
- 抽象基類(lèi)使用場(chǎng)景
-- 接口強(qiáng)制規(guī)定,主要是 強(qiáng)制子類(lèi)實(shí)現(xiàn)某個(gè)方法瞳遍,否則就提示報(bào)錯(cuò)闻妓; - 抽象基類(lèi)的有點(diǎn):
-- 處理繼承問(wèn)題方面更加規(guī)范、系統(tǒng)掠械;
-- 明確調(diào)用之間的相互關(guān)系由缆,使得繼承層次更加清晰; - 抽象基類(lèi)的缺點(diǎn):
-- 抽象基類(lèi)在 python 并非在于用來(lái)繼承猾蒂,主要用來(lái)理解 python繼承 的定義均唉,應(yīng)該 盡量使用鴨子模型;
-- 如果一定要繼承接口的話(huà)肚菠,比較 推薦多繼承舔箭,抽象基類(lèi)容易 設(shè)計(jì)過(guò)度;