首頁  >  文章  >  後端開發  >  詳細介紹Python的鴨子類型

詳細介紹Python的鴨子類型

高洛峰
高洛峰原創
2017-02-23 11:18:411555瀏覽

相信python的開發者對於python的鴨子類型比較熟悉,鴨子類型在維基百科中的準確定義是‘是動態類型的一種風格。在這種風格中,一個物件有效的語義,不是由繼承自特定的類別或實現特定的接口,而是由"當前方法和屬性的集合"決定’。所以這篇文章給大家python的鴨子類型。

鴨子類型基本定義

#首先Python不支援多態,也不用支援多態,python是一種多態性語言,崇尚鴨子類型。

以下是維基百科中對鴨子類型得論述:

在程式設計中,鴨子類型(英文:duck typing)是動態類型的風格。在這種風格中,一個物件有效的語義,不是由繼承自特定的類別或實作特定的接口,而是由當前方法和屬性的集合決定。這個概念的名字來自James Whitcomb Riley提出的鴨子測試,「鴨子測試」可以這樣表述:

「當看到一隻鳥走過來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那麼這隻鳥就可以被稱為鴨子。例如,在不使用鴨子類型的語言中,我們可以寫一個函數,它接受一個類型為鴨的對象,並呼叫它的走和叫方法。在使用鴨子類型的語言中,這樣的一個函數可以接受一個任意類型的對象,並呼叫它的走和叫方法。如果這些需要被呼叫的方法不存在,那麼將會引發一個運行時錯誤。任何擁有這樣的正確的走和叫方法的對像都可被函數接受的這種行為引出了以上表述,這種決定類型的方式因此得名。

鴨子類型通常得益於不測試方法和函數中參數的類型,而是依賴文件、清晰的程式碼和測試來確保正確使用。從靜態類型語言轉向動態類型語言的使用者通常試圖添加一些靜態的(在運行之前的)類型檢查,從而影響了鴨子類型的益處和可伸縮性,並約束了語言的動態特性。

python中的具體實作

#下面的程式碼就是一個簡單的鴨子類型

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')

對於一個鴨子類型來說,我們並不關心這個物件的類型本身或是這個類別繼承,而是這個類別是如何被使用的。我們可以透過下面的程式碼來呼叫這些類別的方法。

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

函數接收這個類別的對象,然後並沒有檢查物件的類型,而是直接呼叫這個物件的走和遊的方法,如果所需要的方法不存在就報錯。

具體在python中鴨子類型的體現如下面的程式碼所示

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): #还有更简单的实现,使用生成器或迭代器什么的:)
    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: &#39;Another_iterAbleClass&#39; 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把上述程式碼的實作方法叫做protocol(協定),這些protocol可以看作是通知型的接口,它規定了呼叫方使用該功能要呼叫物件的哪些方法,被呼叫方要實作哪些方法才能完成這個功能。它和java中的介面差異在於java中的介面功能實作需要透過繼承,繼承的類別必須實作介面中的所有的抽象方法,所以在Java中強調的是類型的概念,而python中的

protocol

更多的是通知性的,一個函數規定要實作某個功能需要呼叫傳入物件的哪些方法,所有實作這些方法的類別就可以實現這個功能。

具體從上面兩個類別來說,第一個類別實作了__getitem__

方法,那python的解釋器就會把它當做一個collection,就可以在這個類別的對象上使用切片,取得子項等方法,第二個類別實作了

__iter__next方法,python就會認為它是一個iterator,就可以在這個類別的物件上透過循環來獲取各個子項。一個類別可以實現它有能力實現的方法,並且只能被用於在它有意義的情況下。 這兩個類別和上面的鴨子類別相比較,其實用於切邊的[](它其實調用的是python的slice

函數)和用於循環的

it​​er()就相當於watch_duck函數,這些函數都會接收任意類別的對象,並呼叫實作功能所需的物件中的方法來實作功能,若函數中呼叫的方法對象裡面不存在,就報錯。 從上面可以看出,python鴨子類型的靈活性在於它關注的是這個所調用的物件是如何被使用的,而沒有關注物件類型的本身是什麼。所以在python中使用isinstance

來判斷傳入參數的類型是不提倡的,更

pythonic的方法是直接使用傳入的參數,透過try,except來處理傳入參數不符合要求的情況。我們應該透過傳入物件的能力而不是傳入物件的類型來使用該物件。

總結

以上就是Python鴨子類型的詳細介紹,本文內容介紹的還是很詳細的,希望對大家學習python能有一定的幫助,如果有疑問大家可以留言交流。

更多詳細介紹Python的鴨子類型相關文章請關注PHP中文網!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn