鸭子键入和(java)接口概念

Kev*_*vin 23 python interface

我刚刚阅读了有关鸭子打字的维基百科文章,我觉得我错过了关于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()
Run Code Online (Sandbox Code Playgroud)

如果,在in_the_forest,我写道:

  • quack像鸭子一样吗?是
  • 它有鸭子feathers吗?是
  • 太棒了,这是我们的鸭子!

后来,因为我知道这是一只鸭子,我想要它swim吗?john会下沉!

我不希望我的应用程序在其进程中间(随机)崩溃只是因为John伪造成一只鸭子,但我想当我收到它时检查对象的每个属性都不是一个明智的想法. ......?

Ben*_*Ben 21

鸭子打字并不是关于检查你需要的东西是否存在然后使用它们.鸭子打字只是使用你需要的东西.

in_the_forest功能是由一个正在考虑鸭子的开发人员编写的.它被设计为在a上运行Duck.一个Duckquackfeathers,所以编码器中使用这些功能让手头工作做好.在这种情况下,a Duck也可以swim不被使用,并且不需要.

在像Java这样的静态语言中,in_the_forest本来会被声明为a Duck.当编码器后来发现他们有一个Person(这可能也quackfeathers),并希望重用的功能,他们是出于运气.是Person一个子类Duck吗?不,这似乎并不合适.有QuacksAndFeathers接口吗?也许,如果我们幸运的话.否则我们必须制作一个,修改Duck以实现它,并修改in_the_forest为取QuacksAndFeathers而不是Duck.如果Duck在外部库中,这可能是不可能的.

在Python中,你只需将你的Person传递给in_the_forest它就可以了.因为事实证明in_the_forest不需要a Duck,它只需要一个"鸭子般的"对象,在这种情况下,Person就像鸭子一样.

game但是,需要一个不同的"鸭子"定义,这个定义稍微强一些.在这里,约翰史密斯运气不好.

现在,Java确实会在编译时捕获到这个错误而Python不会.这可以被视为一个缺点.亲动态类型相反的观点是说,你写的代码,任何实质性的身体总是包含臭虫没有编译器可以捕捉(和说实话,Java是不是具有很强的静态检查一个编译器甚至是很好的例子抓住很多错误).因此,您需要测试代码以找到这些错误.如果你正在测试这些错误,你将很Person容易找到你传递给需要的函数的错误Duck.鉴于此,动态打字员说,一种诱惑你进行测试的语言因为它发现了一些琐碎的错误实际上是一件坏事.最重要的是,它会阻止你做一些非常有用的事情,比如重用一个in_the_forest函数Person.

就个人而言,我被撕成了两个方向.我非常喜欢Python,它具有灵活的动态类型.我非常喜欢Haskell和Mercury的强大静态类型系统.我不是Java或C++的粉丝; 在我看来,他们拥有静态类型的所有坏点,只有很少的好位.

  • 有些人认为静态类型是一种内置的单元测试系统,但我并不是真的很想念它.它就像在现实生活中一样:如果一个人在随机收集的物体中醒来并看到一只鸭子坐在他们旁边,那么一回事就会出现严重错误. (4认同)
  • Python也不会阻止你省略单元测试.如果不包括单元测试,它会突然停止工作吗?不.是否单元测试不依赖于语言(甚至最流行的单元测试框架之一,jUnit,是为Java编写的).`亲动态类型计数器的论点是说你编写的任何实体代码总是包含没有编译器可以捕获的错误:只是因为我们有一天会死,并不意味着我们不应该努力过健康的生活.当我们尝试开始重构时,IMO,动态语言是痛苦的. (2认同)

mac*_*mac 5

不能说其他语言,但在python中它最近(v2.6)引入了抽象基类(ABC)模块.

如果您阅读其介绍背后的基本原理(PEP 3119),您将很快意识到部分原因是"将约翰从肯定的死亡中拯救出来"或换句话说,当您编程到界面时,便于检查事实,所有接口方法将在那里.从链接的PEP:

ABCs只是Python类,它们被添加到对象的继承树中,以向外部检查器发送该对象的某些特征.使用isinstance()进行测试,并且特定ABC的存在意味着测试已通过.此外,ABCs定义了一组最小的方法,用于建立类型的特征行为.根据ABC类型区分对象的代码可以相信这些方法将始终存在.

通常,您可以为自己的代码应用相同的模式.例如:您可以BasePlugin使用插件工作所需的所有方法创建一个类,然后可以通过子类化创建几个不同的插件.根据每个插件是否必须可以定义这些方法,您可以定义BasePlugin静默传递的方法(插件可以定义这些方法)或引发异常(插件必须定义这些方法/覆盖BasePlugin其中的方法).

编辑:在下面的评论主题中,我被建议在答案中包括这一点讨论:

这种功能 - 至少在python中 - 并不是为了人类程序员而实现的(python从不沉默错误,因此已经有很多反馈),而是为了python自己的内省能力(从而使得更容易编写动态加载,元编程代码等...).换句话说:我知道John无法飞行......但我希望python翻译也能知道它!:)


Hei*_*nzi 5

我不希望我的应用程序在其过程中(随机)崩溃,因为 John 伪装成一只鸭子,但我想在我收到对象时检查对象的每个属性并不是一个明智的主意……?

这通常是动态类型的问题。在像 Java 这样的静态类型语言中,编译器在编译时检查是否Person实现IDuck。在像 Python 这样的动态类型语言中,如果Person错过了某些特定的鸭子功能(例如swim),则会出现运行时错误。引用另一篇维基百科文章(“类型系统”,“动态类型”部分):

动态类型可能会导致运行时类型错误——也就是说,在运行时,一个值可能具有意外的类型,并且对该类型应用了无意义的操作。此类错误可能发生在发生编程错误的地方很久之后——也就是说,错误类型的数据传入了它不应该存在的地方。这可能会使错误难以定位。

动态类型有它的缺点(你已经提到了一个)和它的优点。可以在维基百科的类型系统文章的另一部分中找到简要比较:实践中的静态和动态类型检查