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

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