2011-07-05 87 views
22

我剛纔讀了維基百科的文章關於duck typing,我覺得我想念的界面概念,我在Java中使用到的重要一點:鴨打字和(JAVA)的接口概念

"When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck." 


class Duck: 
    def quack(self): 
     print("Quaaaaaack!") 
    def feathers(self): 
     print("The duck has white and gray feathers.") 
    def swim(self): 
     print("Swim seamlessly in the water") 

class Person: 
    def quack(self): 
     print("The person imitates a duck.") 
    def feathers(self): 
     print("The person takes a feather from the ground and shows it.") 
    def name(self): 
     print("John Smith") 

def in_the_forest(duck): 
    duck.quack() 
    duck.feathers() 

def game(): 
    donald = Duck() 
    john = Person() 
    in_the_forest(donald) 
    in_the_forest(john) 

game() 

如果什麼,在in_the_forest,我寫:

  • 做它quack如魚得水?是
  • 它有一隻鴨子feathers?是的
  • 太棒了,這是我們的鴨子!

以後,因爲我知道這是一隻鴨子,我想要它swimjohn會下沉!

我不希望我的應用程序在其進程中間崩潰(隨機),只是因爲John僞造成鴨子,但我想檢查對象的每個屬性都不是明智的做法當我收到它...?

+0

+1:提出的問題 – msw

+1

總之,你不用擔心。您編寫的測試涵蓋了儘可能多的代碼路徑和角落案例,但是100%的安全性是不可能的。並且請注意,Java或其他大多數靜態類型系統無法保證100%的安全性) – delnan

+1

+1:關於重要主題的一個非常好的問題,重中之重。對不起,我只能給它1 upvote。 – Dave

回答

19

鴨子打字並不是真的在檢查你需要的東西是否存在然後使用它們。鴨子打字只是使用你需要的東西。

in_the_forest函數是由一個正在考慮鴨子的開發人員編寫的。它被設計爲在Duck上運行。 A Duck可以是quackfeathers,所以編碼器使用這些功能來完成手頭的工作。在這種情況下,Duck也可以不使用swim這一事實,並且不需要。

在像Java那樣的靜態語言中,in_the_forest本應聲明爲Duck。當編碼器後來發現他們有一個Person(也可能是quackfeathers)並想重用該功能時,它們運氣不佳。 PersonDuck的一個子類?不,這看起來並不合適。是否有一個QuacksAndFeathers接口?也許,如果我們幸運的話。否則,我們將不得不做一個,去修改Duck來實現它,並修改in_the_forest採取QuacksAndFeathers而不是Duck。如果Duck位於外部庫中,這可能是不可能的。

在Python中,您只需將您的Person傳遞給in_the_forest即可。因爲事實證明in_the_forest不需要Duck,它只需要一個「鴨子般」的對象,在這種情況下,Person足夠像鴨子一樣。

game雖然,需要一個不同的「鴨樣」的定義,這稍微更強。在這裏,約翰史密斯運氣不佳。

現在的確,Java在編譯時會遇到這個錯誤,Python不會。這可以被視爲一個缺點。親動態類型的反說法是說你編寫的任何大量代碼總是會包含編譯器可以捕獲的錯誤(並且說實話,Java甚至不是編譯器具有強壯性的特別好例子靜態檢查以捕獲大量的錯誤)。所以你需要測試你的代碼來找到這些錯誤。如果您正在測試這些錯誤,那麼您會發現在您將Person傳遞給需要Duck的函數的錯誤。鑑於此,動態打字員說,嘴饞你進入測試,因爲它發現一些瑣碎的蟲子語言實際上是一種事情。最重要的是,它可以防止你做一些真正有用的事情,比如在Person上重複使用in_the_forest函數。

就我個人而言,我在兩個方向撕裂。我非常喜歡Python,它具有靈活的動態類型。我真的很喜歡Haskell和Mercury他們強大的靜態類型系統。我不是Java或C++的粉絲;在我看來,他們擁有靜態打字的所有壞點,而很少有優點。

+4

有人認爲靜態類型是一種內置的單元測試系統,但我不要錯過它。就像在現實生活中一樣:如果一個人隨意收集物體並且看着旁邊坐着一隻鴨子,就會發現一些事情在很久以前就發生了嚴重的錯誤。 – detly

+1

哦,等等,當我想讓Eclipse的自動完成功能向我展示有用的東西時,我很想念它。而已。 – detly

+0

帶有免費肥皂箱的+1:你想用安全語言填充類型安全嗎?如果你不想看到坐在你旁邊的鴨子(並且沒有人想要遠程整數(並且枚舉高達2^16將會很愚蠢)),那麼你需要Pascal的整數類型「 – msw

5

不能說其他語言,但在Python中它已經在最近(v2.6)推出了Abstract Base Classes (ABC) module

如果您閱讀了其介紹背後的基本原理(PEP 3119),您很快就會意識到部分原因是「從確定的死亡中拯救約翰」,換句話說,爲了便於在編程時檢查事實接口,所有的接口方法將在那裏。從鏈接PEP:

基本知識僅僅是 加入到某個對象的繼承 樹以通知反對外部檢查的 某些功能Python類。 使用isinstance()和 進行測試,測試已經通過了特定的ABC意味着 。另外,ABCs定義了建立該類型的特徵行爲的最小組方法。 在其ABC類型上根據 區分對象的代碼可以相信那些方法將始終存在。

一般來說,您可以將相同的模式應用於您自己的代碼。例如:您可以使用插件所需的所有方法創建一個類,然後可以通過繼承子類來創建多個不同的插件。根據是否每個插件必須可以有那些方法來定義,你可以定義BasePlugin方法默默地傳遞(插件可以定義的方法),要麼引發異常(插件必須定義這些方法/覆蓋BasePlugin的一個)。

編輯:在下面的評論線程,我已經提出在的答案,包括該位的討論:

這種功能 - 至少在蟒蛇 - 沒有爲實現爲了人類的程序員(python從不沉默一個錯誤,所以已經有很多反饋),而是爲了python自己的內省功能(因此使編寫動態加載,元編程代碼等更容易)。換句話說:我知道約翰不能飛,但我希望Python解釋器也知道它! :)

+0

僅僅因爲你可以使用模擬靜態類型的Java-isms並不意味着你應該這樣做。我還沒有看到非玩具類的實現,即異構列表的用戶不知道John是否可以飛行。 此外,假設你的'isinstance'檢查失敗,你必須應付它。這與「except」塊有何不同? (「繼承可能是OOP中最具誘惑性過度使用的特性之一」-me) – msw

+0

@msw - 我不知道java如此平行並不能幫助我理解你的觀察(如果你有雖然有機會重述)!)。請記住,大多數這種功能 - 至少在python中 - 並不是爲了程序員的目的而實現的(python從不沉默一個錯誤,所以在那裏已經有很多反饋),而是爲了Python的擁有者自省能力(從而更容易編寫動態加載,元編程代碼等)。換句話說:我知道約翰不能飛,但我希望Python解釋器也知道它! :) – mac

+0

Python解釋器確實知道它。我會在http://stackoverflow.com/questions/6576837/is-enforcing-an-abstract-method-implementation-unpythonic/6576986#6576986中重述我的答案,但是覺得我今天正在成爲一個蟎蟲教條。我的「Java-ism」可以應用於許多當前流行的OOP語言(例如C++)。我發現http://dirtsimple.org/2004/12/python-is-not-java.html有助於打破舊思維。關於元編程等等的觀點應該比'非元程序員'更明顯(但是C++ dynamic_casts應該如此(爲什麼該功能如果不用的話)) – msw

3

我不希望我的應用程序崩潰(隨機),在其過程中的中間只是因爲約翰假裝是鴨子,但我想這不會是一個明智的想法,以檢查當我收到它時,對象的每個屬性......?

這是一個問題動態打字一般。在像Java這樣的靜態類型語言中,編譯器在編譯時檢查Person是否實現了IDuck。在像Python這樣的動態類型語言中,如果Person錯過了某些特定的鴨子功能(例如swim),則會出現運行時錯誤。引用另一個維基百科文章("Type system", Section "Dynamic Typing"):

動態類型可能導致運行時類型的錯誤,也就是說,在運行時,一個值可能有意想不到的類型,並且被施加無意義爲該類型的操作。這種錯誤可能發生在編程錯誤發生的地方之後很長時間,即錯誤類型的數據傳遞到它不應該有的地方。這可能會使該錯誤難以定位。

動態打字有其缺點(你提到了一個)及其優點。在維基百科的類型系統文章的另一部分中可以找到簡要的比較:Static and dynamic type checking in practice