鸭子类型在软件开发中意味着什么?


当前回答

我看到很多答案都在重复这句老话:

如果它长得像鸭子,叫声也像鸭子,那它就是鸭子

然后深入解释可以使用duck typing做什么,或者一个似乎进一步混淆了概念的示例。

我觉得没什么用。

这是我发现的关于鸭子打字的最好的简单英语回答:

Duck Typing意味着对象是根据它能做什么来定义的,而不是根据它能做什么来定义的 它是什么。

这意味着我们不太关心对象的类/类型,而更关心可以对其调用哪些方法以及可以对其执行哪些操作。我们不关心它的类型,我们只关心它能做什么。

其他回答

它是一个用于没有强类型的动态语言中的术语。

其思想是,为了调用对象上的现有方法,您不需要指定类型——如果在对象上定义了方法,则可以调用它。

这个名字来源于一句话“如果它看起来像鸭子,叫起来像鸭子,那它就是鸭子”。

维基百科有更多的信息。

Duck typing 意味着一个操作没有正式指定其操作数必须满足的要求,而只是使用给定的条件进行尝试。

与其他人所说的不同,这并不一定与动态语言或继承问题有关。

示例任务:在对象上调用某个方法Quack。

如果不使用duck-typing,执行此任务的函数f必须事先指定其参数必须支持某种方法Quack。常用的方法是使用接口

interface IQuack { 
    void Quack();
}

void f(IQuack x) { 
    x.Quack(); 
}

调用f(42)失败,但f(donald)工作,只要donald是一个iquack子类型的实例。

另一种方法是结构类型—但同样,方法Quack()是正式指定的,任何不能提前证明它是嘎嘎作响的东西都会导致编译器失败。

def f(x : { def Quack() : Unit }) = x.Quack() 

我们甚至可以写成

f :: Quackable a => a -> IO ()
f = quack

在Haskell中,Quackable类型类确保了方法的存在。


So how does **duck typing** change this?

好吧,正如我所说的,duck输入系统不指定需求,而只是尝试任何可行的方法。

因此,像Python这样的动态类型系统总是使用duck类型:

def f(x):
    x.Quack()

如果f得到一个支持Quack()的x,一切正常,否则,它将在运行时崩溃。

但是鸭子类型并不意味着动态类型——事实上,有一种非常流行但完全静态的鸭子类型方法,它也没有给出任何要求:

template <typename T>
void f(T x) { x.Quack(); } 

该函数没有以任何方式告诉它想要某个可以Quack的x,所以它只是在编译时尝试,如果一切正常,就没问题。

假设您正在设计一个简单的函数,该函数获取一个Bird类型的对象并调用它的walk()方法。你可以考虑两种方法:

这是我的函数,我必须确保它只接受Bird类型,否则代码将无法编译。如果有人想使用我的功能,他们必须知道我只接受鸟类。 我的函数获取任何对象,我只调用对象的walk()方法。因此,如果对象可以walk(),那么它是正确的。如果不能,我的函数就会失败。所以,这里的对象是鸟还是其他东西并不重要,重要的是它能走路()(这是鸭子类型)。

必须考虑到鸭子类型在某些情况下可能是有用的。例如,Python经常使用duck类型。


读有用的书

在Java、Python和JavaScript中都有很好的鸭子类型的例子 https://en.wikipedia.org/wiki/Duck_typing等。 这里也有一个很好的答案,它描述了动态的优点 动态类型及其缺点:动态类型所能带来的生产力提升是什么?

Duck typing:

如果它像鸭子一样说话和走路,那么它就是一只鸭子

这通常被称为诱拐(诱拐推理或也称为归纳,我认为一个更清晰的定义):

从C(结论,我们所看到的)和R(规则,我们所知道的),我们接受/决定/假设P(前提,属性),换句话说,一个给定的事实 ... 医学诊断的基础 和鸭子:C =走路,说话,R =像鸭子,P =它是一只鸭子

回到编程:

对象o有方法/属性mp1和接口/类型T 要求/定义mp1 对象o有方法/属性mp2,接口/类型T要求/定义mp2 ...

因此,不仅仅是简单地接受mp1…在任何对象上,只要它满足mp1的某些定义…,编译器/运行时也应该接受断言o是类型T

上面的例子是这样的吗?Duck输入实质上就是没有输入吗?或者我们应该称之为隐式类型?

我知道我没有给出一个笼统的答案。在Ruby中,我们不声明变量或方法的类型——所有东西都是某种类型的对象。 规则是"类不是类型"

在Ruby中,类从来都不是(好吧,几乎从来都不是)类型。相反,对象的类型更多地由该对象可以做什么来定义。在Ruby中,我们称之为duck typing。如果一个对象像鸭子一样走路,像鸭子一样说话,那么解释器很乐意把它当作鸭子来对待。

例如,您可能正在编写一个例程,将歌曲信息添加到字符串中。如果你有c#或Java背景,你可能会这样写:

def append_song(result, song)
    # test we're given the right parameters 
    unless result.kind_of?(String)
        fail TypeError.new("String expected") end
    unless song.kind_of?(Song)
        fail TypeError.new("Song expected")
end

result << song.title << " (" << song.artist << ")" end
result = ""

append_song(result, song) # => "I Got Rhythm (Gene Kelly)"

采用Ruby的鸭子输入,您可以编写一些简单得多的东西:

def append_song(result, song)
    result << song.title << " (" << song.artist << ")"
end

result = ""
append_song(result, song) # => "I Got Rhythm (Gene Kelly)"

你不需要检查参数的类型。如果它们支持<<(在结果的情况下)或标题和艺术家(在歌曲的情况下),一切都会正常工作。如果没有,你的方法无论如何都会抛出一个异常(就像你检查了类型一样)。但如果没有检查,你的方法突然变得灵活多了。你可以向它传递一个数组、一个字符串、一个文件或任何其他使用<<追加的对象,它就可以工作了。