‘那只東西呱呱的叫,有扁扁的嘴巴勃救,走起路來(lái)還外八碍讨,對(duì)!它就是只鴨子’
基本定義
對(duì)于熟悉python的開(kāi)發(fā)者來(lái)說(shuō)蒙秒,相信對(duì)于python的鴨子類型比較熟悉勃黍,所謂鴨子類型,在維基百科中的準(zhǔn)確定義是‘是動(dòng)態(tài)類型的一種風(fēng)格晕讲。在這種風(fēng)格中覆获,一個(gè)對(duì)象有效的語(yǔ)義,不是由繼承自特定的類或?qū)崿F(xiàn)特定的接口瓢省,而是由"當(dāng)前方法和屬性的集合"決定’弄息。python中的具體實(shí)現(xiàn)
下面的代碼就是一個(gè)簡(jiǎn)單的鴨子類型
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')
對(duì)于一個(gè)鴨子類型來(lái)說(shuō),我們并不關(guān)心這個(gè)對(duì)象的類型本身或是這個(gè)類繼承勤婚,而是這個(gè)類是如何被使用的摹量。我們可以通過(guò)下面的代碼來(lái)調(diào)用這些類的方法。
def watch_duck(animal):
animal.walk()
animal.swim()
small_duck = duck()
watch_duck(small_duck)
output >>
I walk like a duck
i swim like a duck
duck_like_man = person()
watch_duck(duck_like_man)
output >>
this one walk like a duck
this man swim like a duck
class Lame_Foot_Duck():
def swim(self):
print('i am lame but i can swim')
lame_duck = Lame_Foot_Duck()
watch_duck(lame_duck)
output >>
AttributeError: Lame_Foot_Duck instance has no attribute 'walk'
watch_duck函數(shù)接收這個(gè)類的對(duì)象蛔六,然后并沒(méi)有檢查對(duì)象的類型荆永,而是直接調(diào)用這個(gè)對(duì)象的走和游的方法,如果所需要的方法不存在就報(bào)錯(cuò)国章。具體在python中鴨子類型的體現(xiàn)如下面的代碼所示
class CollectionClass():
lists = [1,2,3,4]
def __getitem__(self, index):
return self.lists[index]
iter_able_object = CollectionClass()
class Another_iterAbleClass():
lists=[1,2,3,4]
list_position = -1
def __iter__(self):
return self
def next(self): #還有更簡(jiǎn)單的實(shí)現(xiàn)具钥,使用生成器或迭代器什么的:)
self.list_position += 1
if self.list_position >3:
raise StopIteration
return self.lists[self.list_position]
another_iterable_object=Another_iterAbleClass()
print(iter_able_object[1])
print(iter_able_object[1:3])
output>>
2
[2, 3]
another_iterable_object[2]
output>>
Traceback (most recent call last):
File "/Users/steinliber/a.py", line 32, in <module>
another_iterable_object[2]
TypeError: 'Another_iterAbleClass' object does not support indexing
print(next(another_iterable_object))
output>>
1
print(next(another_iterable_object))
output>>
2
print(next(iter_able_object))
output>>
Traceback (most recent call last):
File "/Users/steinliber/a.py", line 29, in <module>
print(next(iter_able_object))
TypeError: IterAbleClass object is not an iterator
在python把上述代碼的實(shí)現(xiàn)方法叫做protocol(協(xié)議),這些protocol可以看作是通知型的接口液兽,它規(guī)定了調(diào)用方使用該功能要調(diào)用對(duì)象的哪些方法骂删,被調(diào)用方要實(shí)現(xiàn)哪些方法才能完成這個(gè)功能掌动。它和java中的接口區(qū)別在于java中的接口功能實(shí)現(xiàn)需要通過(guò)繼承,繼承的類必須實(shí)現(xiàn)接口中的所有的抽象方法宁玫,所以在Java中強(qiáng)調(diào)的是類型的概念粗恢,而python中的protocol更多的是通知性的,一個(gè)函數(shù)規(guī)定要實(shí)現(xiàn)某個(gè)功能需要調(diào)用傳入對(duì)象的哪些方法欧瘪,所有實(shí)現(xiàn)這些方法的類就可以實(shí)現(xiàn)這個(gè)功能眷射。
具體從上面兩個(gè)類來(lái)說(shuō),第一個(gè)類實(shí)現(xiàn)了__getitem__方法,那python的解釋器就會(huì)把它當(dāng)做一個(gè)collection佛掖,就可以在這個(gè)類的對(duì)象上使用切片,獲取子項(xiàng)等方法妖碉,第二個(gè)類實(shí)現(xiàn)了__iter__和next方法,python就會(huì)認(rèn)為它是一個(gè)iterator芥被,就可以在這個(gè)類的對(duì)象上通過(guò)循環(huán)來(lái)獲取各個(gè)子項(xiàng)欧宜。一個(gè)類可以實(shí)現(xiàn)它有能力實(shí)現(xiàn)的方法,并只能被用于在它有意義的情況下拴魄。
這兩個(gè)類和上面的鴨子類相比較冗茸,其實(shí)用于切邊的[](它其實(shí)調(diào)用的是python的slice函數(shù))和用于循環(huán)的iter()就相當(dāng)于watch_duck函數(shù),這些函數(shù)都接收任意類的對(duì)象匹中,并調(diào)用實(shí)現(xiàn)功能所需要的對(duì)象中的方法來(lái)實(shí)現(xiàn)功能夏漱,若該函數(shù)中調(diào)用的方法對(duì)象里面不存在,就報(bào)錯(cuò)顶捷。
從上面可以看出麻蹋,python鴨子類型的靈活性在于它關(guān)注的是這個(gè)所調(diào)用的對(duì)象是如何被使用的,而沒(méi)有關(guān)注對(duì)象類型的本身是什么焊切。所以在python中使用isinstance來(lái)判斷傳入?yún)?shù)的類型是不提倡的,更pythonic的方法是直接使用傳入的參數(shù)芳室,通過(guò)try专肪,except來(lái)處理傳入?yún)?shù)不符合要求的情況。我們應(yīng)該通過(guò)傳入對(duì)象的能力而不是傳入對(duì)象的類型來(lái)使用該對(duì)象堪侯。