2011-07-05 54 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