淺談Python鴨子類(lèi)型
目錄
1、什么是“鴨子類(lèi)型”
2、“鴨子類(lèi)型”從何而來(lái)
2.1 多態(tài)
2.2 多態(tài)的使用
2.3 鴨子類(lèi)型產(chǎn)生
3、小結(jié)

1、什么是“鴨子類(lèi)型”
Python崇尚“鴨子類(lèi)型”
對(duì)于鴨子類(lèi)型常見(jiàn)的說(shuō)法是:“當(dāng)看到一只鳥(niǎo)走起來(lái)像鴨子、游泳起來(lái)像鴨子、叫起來(lái)也像鴨子,那么這只鳥(niǎo)就可以被稱為鴨子。”
鴨子類(lèi)型(英語(yǔ):duck typing)在程序設(shè)計(jì)中是動(dòng)態(tài)類(lèi)型的一種風(fēng)格。在這種風(fēng)格中,一個(gè)對(duì)象有效的語(yǔ)義,不是由繼承自特定的類(lèi)或?qū)崿F(xiàn)特定的接口,而是由“當(dāng)前方法和屬性的集合”決定
在鴨子類(lèi)型中,關(guān)注點(diǎn)在于對(duì)象的行為能做什么;而不是關(guān)注對(duì)象所屬的類(lèi)型。例如,在不使用鴨子類(lèi)型的語(yǔ)言中,我們可以編寫(xiě)一個(gè)函數(shù),它接受一個(gè)類(lèi)型為"鴨子"的對(duì)象,并調(diào)用它的"走"和"叫"方法。在使用鴨子類(lèi)型的語(yǔ)言中,這樣的一個(gè)函數(shù)可以接受一個(gè)任意類(lèi)型的對(duì)象,并調(diào)用它的"走"和"叫"方法。如果這些需要被調(diào)用的方法不存在,那么將引發(fā)一個(gè)運(yùn)行時(shí)錯(cuò)誤。任何擁有這樣的正確的"走"和"叫"方法的對(duì)象都可被函數(shù)接受的這種行為引出了以上表述,這種決定類(lèi)型的方式因此得名
鴨子類(lèi)型通常得益于"不"測(cè)試方法和函數(shù)中參數(shù)的類(lèi)型,而是依賴文檔、清晰的代碼和測(cè)試來(lái)確保正確使用
在常規(guī)類(lèi)型中,我們能否在一個(gè)特定場(chǎng)景中使用某個(gè)對(duì)象取決于這個(gè)對(duì)象的類(lèi)型,而在鴨子類(lèi)型中,則取決于這個(gè)對(duì)象是否具有某種屬性或者方法——即只要具備特定的屬性或方法,能通過(guò)鴨子測(cè)試,就可以使用。
內(nèi)容參考自維基百科:https://zh.wikipedia.org/wiki/%E9%B8%AD%E5%AD%90%E7%B1%BB%E5%9E%8B
2、“鴨子類(lèi)型”從何而來(lái)
2.1 多態(tài)
先來(lái)看看“多態(tài)”
大學(xué)時(shí)學(xué)習(xí)過(guò)C、Java基礎(chǔ)這一類(lèi)強(qiáng)類(lèi)型語(yǔ)言,面向?qū)ο缶幊痰娜筇匦灾挥袀€(gè)概念叫做“多態(tài)”
簡(jiǎn)單來(lái)說(shuō),定義時(shí)的類(lèi)型和運(yùn)行時(shí)的類(lèi)型不一樣就是多態(tài)
更通俗的來(lái)說(shuō),多態(tài)是指一類(lèi)事物有多種形態(tài)。比如動(dòng)物有多種形態(tài),人、狗、貓等等
放到二進(jìn)制的世界,例如文件,文件有多種形態(tài):文本文件、可執(zhí)行文件
總而言之,多態(tài)即某類(lèi)的再分類(lèi),再分的每一類(lèi)就是父類(lèi)的多種形態(tài)的一種
例如下面代碼示例:
程序在運(yùn)行到
list這一步是只把Cat,Dog,Duck當(dāng)成變量只有在最后實(shí)例化調(diào)用類(lèi)方法的時(shí)候才會(huì)明白是一個(gè)類(lèi),這就表示多態(tài)
class Cat(object):
def info(self):
print("我是一只貓")
class Dog(object):
def info(self):
print("我是一只狗")
class Duck(object):
def info(self):
print("我是一只鴨")
list=[Cat,Dog,Duck]
for animal in list:
animal().info()
2.2 多態(tài)的使用
再來(lái)一個(gè)偏實(shí)際的應(yīng)用示例代碼
from abc import ABCMeta,abstractmethod #(抽象方法)
class Payment(metaclass=ABCMeta): # metaclass 元類(lèi) metaclass = ABCMeta表示Payment類(lèi)是一個(gè)規(guī)范類(lèi)
def __init__(self,name,money):
self.money=money
self.name=name
@abstractmethod # @abstractmethod表示下面一行中的pay方法是一個(gè)必須在子類(lèi)中實(shí)現(xiàn)的方法
def pay(self,*args,**kwargs):
pass
class AliPay(Payment):
def pay(self):
# 支付寶提供了一個(gè)網(wǎng)絡(luò)上的聯(lián)系渠道
print('%s通過(guò)支付寶消費(fèi)了%s元'%(self.name,self.money))
class WeChatPay(Payment):
def pay(self):
# 微信提供了一個(gè)網(wǎng)絡(luò)上的聯(lián)系渠道
print('%s通過(guò)微信消費(fèi)了%s元'%(self.name,self.money))
class Order(object):
def account(self,pay_obj):
pay_obj.pay()
pay_obj = WeChatPay("wang",100)
pay_obj2 = AliPay("zhang",200)
order = Order()
order.account(pay_obj)
order.account(pay_obj2)
從上述代碼可見(jiàn),在調(diào)用order對(duì)象的account()方法時(shí),程序并不關(guān)心為該方法傳入的參數(shù)對(duì)象pay_obj是誰(shuí),只要求此參數(shù)對(duì)象pay_obj包含pay()方法即可,而調(diào)用者傳入的參數(shù)對(duì)象類(lèi)型pay_obj是子類(lèi)對(duì)象還是其他類(lèi)對(duì)象,對(duì)Python來(lái)說(shuō)無(wú)所謂
多態(tài)性就是相同的消息(函數(shù)方法觸發(fā))使得不同的類(lèi)做出不同的響應(yīng),這就是典型的類(lèi)編程中多態(tài)性的應(yīng)用實(shí)例
2.3 鴨子類(lèi)型產(chǎn)生
在上面的例子中order.account(pay_obj)中pay_obj不需要類(lèi)型聲明,而java在使用時(shí)要定義好類(lèi)型
(order.account(Payment pay_obj)),所以你傳入別的類(lèi)型對(duì)象一定報(bào)錯(cuò)
但是python因?yàn)槭莿?dòng)態(tài)語(yǔ)言所以傳入的對(duì)象只要擁有調(diào)用的方法即可視為Payment類(lèi)型對(duì)象,這就是所謂的鴨子類(lèi)型
class Duck():
def walk(self):
print('I walk like a duck')
def swim(self):
print('i swim like a duck')
class Person():
def walk(self):
print('this one walk like a duck')
def swim(self):
print('this man swim like a duck')
可以很明顯的看出,Person類(lèi)擁有跟Duck類(lèi)一樣的方法,當(dāng)有一個(gè)函數(shù)調(diào)用Duck類(lèi),并利用到了兩個(gè)方法walk()和swim()。我們傳入Person類(lèi)也一樣可以運(yùn)行,函數(shù)并不會(huì)檢查對(duì)象的類(lèi)型是不是Duck,只要他擁有walk()和swim()方法,就可以正確的被調(diào)用
3、小結(jié)
在鴨子類(lèi)型中,關(guān)注的不是對(duì)象的類(lèi)型本身,而是它是如何使用的
例如,在不使用鴨子類(lèi)型的語(yǔ)言中,我們可以編寫(xiě)一個(gè)函數(shù),它接受一個(gè)類(lèi)型為"鴨子"的對(duì)象,并調(diào)用它的"走"和"叫"方法
在使用鴨子類(lèi)型的語(yǔ)言中,這樣的一個(gè)函數(shù)可以接受一個(gè)任意類(lèi)型的對(duì)象,并調(diào)用它的"走"和"叫"方法
如果這些需要被調(diào)用的方法不存在,那么將引發(fā)一個(gè)運(yùn)行時(shí)錯(cuò)誤
任何擁有這樣的正確的"走"和"叫"方法的對(duì)象都可被函數(shù)接受的這種行為引出了以上表述,這種決定類(lèi)型的方式因此得名
鵝不是鴨子,但是鵝一旦擁有鴨子的一些方法,就可以被當(dāng)成鴨子類(lèi)型了
鴨子類(lèi)型的好處就在于能夠避免一些類(lèi)的重寫(xiě),無(wú)需大量復(fù)制相同的代碼
鴨子類(lèi)型使用的前提是需要良好的文檔支持,不然會(huì)讓代碼變得很混亂,如果沒(méi)有良好的文檔及說(shuō)明,有可能會(huì)導(dǎo)致你的“鴨子”不是我的“鵝”了
