sus*_*ani 388 programming-languages duck-typing
我在网上阅读随机主题时遇到了鸭子打字这个术语,并没有完全理解它.
什么是"鸭子打字"?
Ode*_*ded 287
我们的想法是,您不需要类型来调用对象上的现有方法 - 如果在其上定义了方法,则可以调用它.
这个名字来自短语"如果它看起来像鸭子,像鸭子一样嘎嘎叫,那就是鸭子".
维基百科有更多信息.
Dar*_*rio 193
鸭子打字 意味着操作没有正式指定其操作数必须满足的要求,而只是尝试使用给定的内容.
与其他人所说的不同,这不一定与动态语言或继承问题有关.
示例任务:Quack在对象上调用某个方法.
在不使用duck-typing的情况下,f执行此任务的函数必须事先指定其参数必须支持某些方法Quack.常见的方法是使用接口
interface IQuack {
void Quack();
}
void f(IQuack x) {
x.Quack();
}
Run Code Online (Sandbox Code Playgroud)
调用f(42)失败,但f(donald)只要donald是IQuack-subtype 的实例就可以工作.
另一种方法是结构类型 - 但同样,该方法Quack()被正式指定为任何无法quack事先证明它的东西都会导致编译器失败.
def f(x : { def Quack() : Unit }) = x.Quack()
Run Code Online (Sandbox Code Playgroud)
我们甚至可以写
f :: Quackable a => a -> IO ()
f = quack
Run Code Online (Sandbox Code Playgroud)
在Haskell中,Quackable类型类确保我们的方法的存在.
好吧,正如我所说,鸭子打字系统没有指定要求,只是尝试任何有效的方法.
因此,Python的动态类型系统总是使用duck typing:
def f(x):
x.Quack()
Run Code Online (Sandbox Code Playgroud)
如果f获得x支持a Quack(),一切都很好,如果没有,它将在运行时崩溃.
但鸭子打字并不意味着动态打字 - 事实上,有一种非常流行但完全静态的鸭子打字方法也没有给出任何要求:
template <typename T>
void f(T x) { x.Quack(); }
Run Code Online (Sandbox Code Playgroud)
该函数没有以任何方式告诉它它想要一些x可以Quack,所以相反它只是在编译时尝试,如果一切正常,那就没关系.
BKS*_*eon 111
讨论这个问题的语义是相当细微的(并且非常学术性),但这里是一般性的想法:
鸭子打字
("如果它像鸭子一样走路,像鸭子那样嘎嘎叫,那就是鸭子了.") - 是的但是这意味着什么?最好通过示例说明:
示例:动态键入的语言
想象一下,我有一根魔杖.它有特殊的权力.如果我挥动魔杖说"开车!" 到了一辆车,那么,它开车!
它适用于其他事情吗?不确定:所以我在卡车上试试.哇 - 它也开车了!然后我在飞机,火车和1个伍兹(它们是一种人们用来"驾驶"高尔夫球的高尔夫球杆)上尝试.他们全都开车!
但它会起作用吗,一个茶杯?错误:KAAAA-BOOOOOOM!没那么好的.====>茶杯不能开车!! 咄!?
这基本上是鸭子打字的概念.这是一个先试后买的类型系统.如果它有效,一切都很好.但如果它失败了,那么它会在你的脸上爆炸.
换句话说,我们对对象可以做什么感兴趣,而不是对象是什么.
示例:静态类型语言
如果我们关心的是物体实际上是什么,那么我们的魔术只能用于预先设定的授权类型 - 在这种情况下是汽车,但在其他可以驱动的物体上会失败:卡车,轻便摩托车,嘟嘟车等.它不适用于卡车,因为我们的魔杖预计它只适用于汽车.
换句话说,在这种情况下,魔杖会非常仔细地看待物体是什么(它是汽车吗?)而不是物体可以做什么(例如汽车,卡车等是否可以驾驶).
你能驾驶卡车开车的唯一方法就是如果你能以某种方式获得魔术棒以期望卡车和汽车(也许是通过"实现一个共同的界面").如果您不知道这意味着什么,那么暂时忽略它.
摘要:关键外卖
鸭子打字的重要之处在于对象实际上可以做什么,而不是对象是什么.
Ali*_*ahi 23
考虑一下你正在设计一个简单的函数,它获取一个类型的对象Bird并调用它的walk()方法.您可以想到两种方法:
Bird的代码,否则它们的代码将无法编译.如果有人想使用我的功能,他必须意识到我只接受Birdobjects,我只是调用对象walk()方法.因此,如果object能walk()正确,如果不能我的函数将失败.所以这里对象是一个Bird或其他任何东西并不重要,重要的是它可以walk() (这是鸭子打字)必须考虑鸭子打字在某些情况下可能有用,例如Python使用鸭子打字很多.
Chr*_*ter 18
维基百科有一个相当详细的解释:
http://en.wikipedia.org/wiki/Duck_typing
duck typing是一种动态类型,其中对象的当前方法和属性集确定有效语义,而不是从特定类或特定接口的实现继承.
重要的注意事项可能是,对于鸭子类型,开发人员更关心的是消耗的对象部分而不是实际的底层类型.
Gab*_*les 10
注意::=可以读作“被定义为”。
“鸭子类型”的意思是:只要在传入的任何对象上尝试该方法(函数调用),而不是先检查对象的类型以查看该方法是否是对此类类型的有效调用。
让我们称之为“尝试方法,不要检查类型”类型,“方法调用类型检查”,或者简称为“方法调用类型”。
在下面更长的解释中,我将更详细地解释这一点,并帮助您理解荒谬、深奥和混淆的术语“鸭子打字”。
更长的解释:
Python 在上面做了这个概念。考虑这个示例函数:
def func(a):
a.method1()
a.method2()
Run Code Online (Sandbox Code Playgroud)
当对象(输入参数a)进入函数时func(),函数应尝试(在运行时)调用此对象上指定的任何方法(即:method1()和method2()上面的示例中),而不是首先检查是否a某些“有效”类型”具有这些方法。
因此,它是运行时基于操作的尝试,而不是编译时或运行时基于类型的检查。
现在看看这个愚蠢的例子:
def func(duck_or_duck_like_object):
duck_or_duck_like_object.quack()
duck_or_duck_like_object.walk()
duck_or_duck_like_object.fly()
Run Code Online (Sandbox Code Playgroud)
于是诞生了荒谬的短语:
如果它走路像鸭子,叫起来像鸭子,那么它就是鸭子。
该方案使用“鸭打字”应简单地尝试叫什么方法的对象上(在本例以上:quack(),walk(),和fly())甚至不知道该类型的对象!它只是尝试方法!如果它们有效,那就太好了,因为所有“鸭子类型”语言都知道或关心它,IT(传递给函数的对象)就是 DUCK!——因为所有(类似鸭子的)方法都适用于它。
(总结我自己的话):
“鸭子类型”语言不应检查其类型(既不在编译时也不在运行时)——它不在乎。它只会在运行时尝试这些方法。如果它们有效,那就太好了。如果他们不这样做,那么它将抛出一个运行时错误。
那就是鸭子打字。
我厌倦了这种荒谬的“鸭子”解释(因为没有这个完整的解释,它根本没有任何意义!),其他人也是如此。示例:来自BKSpurgeon 的回答(我用粗体强调):
(“如果它走路像鸭子,叫起来像鸭子,那么它就是一只鸭子。”) -是的!但是,这是什么意思??!”
现在我明白了:只需在传入的任何对象上尝试该方法,而不是先检查对象的类型。
我将称之为“运行时检查,其中程序只是尝试调用的方法,甚至不知道对象是否具有这些方法,而不是首先检查对象的类型作为知道对象具有这些方法的手段”,因为只是更有意义。但是……说得太长了,所以人们宁愿多年来相互混淆,而不是说诸如“鸭子打字”之类的可笑但引人入胜的事情。
让我们称之为:“尝试方法,不要检查类型”打字。或者,也许:“方法调用类型检查”(简称“方法调用类型”),或“方法调用的间接类型检查”,因为它使用给定方法的调用作为“足够的证据” object 是正确的类型,而不是直接检查对象的类型。
请注意,这种“方法调用类型检查”(否则容易混淆地称为“鸭子类型”)是一种动态类型。但是,并非所有动态类型都必须是“方法调用类型检查”,因为动态类型或运行时类型检查也可以通过实际检查对象的类型而不是简单地尝试调用调用的方法来完成函数中的对象而不知道它的类型。
我看到很多重复旧习语的答案:
如果它看起来像鸭子而嘎嘎像鸭子,那就是鸭子
然后深入说明如何使用鸭类打字,或者举一个例子,进一步混淆了这个概念。
我找不到太多帮助。
这是对我发现的关于鸭类打字的简单英语答案的最佳尝试:
Duck Typing表示对象是由它可以做什么而不是由它是什么定义的。
这意味着我们不太关心对象的类/类型,而更关心可以在对象上调用什么方法以及可以对其执行什么操作。我们不在乎它的类型,我们在乎它可以做什么。