我刚刚阅读了有关鸭子打字的维基百科文章,我觉得我错过了关于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
.一个Duck
能quack
和feathers
,所以编码器中使用这些功能让手头工作做好.在这种情况下,a Duck
也可以swim
不被使用,并且不需要.
在像Java这样的静态语言中,in_the_forest
本来会被声明为a Duck
.当编码器后来发现他们有一个Person
(这可能也quack
和feathers
),并希望重用的功能,他们是出于运气.是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++的粉丝; 在我看来,他们拥有静态类型的所有坏点,只有很少的好位.
不能说其他语言,但在python中它最近(v2.6)引入了抽象基类(ABC)模块.
如果您阅读其介绍背后的基本原理(PEP 3119),您将很快意识到部分原因是"将约翰从肯定的死亡中拯救出来"或换句话说,当您编程到界面时,便于检查事实,所有接口方法将在那里.从链接的PEP:
ABCs只是Python类,它们被添加到对象的继承树中,以向外部检查器发送该对象的某些特征.使用isinstance()进行测试,并且特定ABC的存在意味着测试已通过.此外,ABCs定义了一组最小的方法,用于建立类型的特征行为.根据ABC类型区分对象的代码可以相信这些方法将始终存在.
通常,您可以为自己的代码应用相同的模式.例如:您可以BasePlugin
使用插件工作所需的所有方法创建一个类,然后可以通过子类化创建几个不同的插件.根据每个插件是否必须或可以定义这些方法,您可以定义BasePlugin
静默传递的方法(插件可以定义这些方法)或引发异常(插件必须定义这些方法/覆盖BasePlugin
其中的方法).
编辑:在下面的评论主题中,我被建议在答案中包括这一点讨论:
这种功能 - 至少在python中 - 并不是为了人类程序员而实现的(python从不沉默错误,因此已经有很多反馈),而是为了python自己的内省能力(从而使得更容易编写动态加载,元编程代码等...).换句话说:我知道John无法飞行......但我希望python翻译也能知道它!:)
我不希望我的应用程序在其过程中(随机)崩溃,因为 John 伪装成一只鸭子,但我想在我收到对象时检查对象的每个属性并不是一个明智的主意……?
这通常是动态类型的问题。在像 Java 这样的静态类型语言中,编译器在编译时检查是否Person
实现IDuck
。在像 Python 这样的动态类型语言中,如果Person
错过了某些特定的鸭子功能(例如swim
),则会出现运行时错误。引用另一篇维基百科文章(“类型系统”,“动态类型”部分):
动态类型可能会导致运行时类型错误——也就是说,在运行时,一个值可能具有意外的类型,并且对该类型应用了无意义的操作。此类错误可能发生在发生编程错误的地方很久之后——也就是说,错误类型的数据传入了它不应该存在的地方。这可能会使错误难以定位。
动态类型有它的缺点(你已经提到了一个)和它的优点。可以在维基百科的类型系统文章的另一部分中找到简要比较:实践中的静态和动态类型检查。
归档时间: |
|
查看次数: |
3294 次 |
最近记录: |