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


当前回答

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

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

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

维基百科有更多的信息。

其他回答

鸭子打字不是类型提示!

基本上,为了使用“duck typing”,你不会针对特定的类型,而是通过使用公共接口来针对更广泛的子类型(不是谈论继承,当我指的子类型时,我指的是适合相同配置文件的“事物”)。

你可以想象一个存储信息的系统。为了读写信息,你需要某种存储空间和信息。

存储类型可以是:文件、数据库、会话等。

无论存储类型是什么,该接口都会让您知道可用的选项(方法),这意味着在这一点上什么都没有实现!换句话说,接口不知道如何存储信息。

每个存储系统都必须通过实现接口的相同方法来知道接口的存在。

interface StorageInterface
{
   public function write(string $key, array $value): bool;
   public function read(string $key): array;
}


class File implements StorageInterface
{
    public function read(string $key): array {
        //reading from a file
    }

    public function write(string $key, array $value): bool {
         //writing in a file implementation
    }
}


class Session implements StorageInterface
{
    public function read(string $key): array {
        //reading from a session
    }

    public function write(string $key, array $value): bool {
         //writing in a session implementation
    }
}


class Storage implements StorageInterface
{
    private $_storage = null;

    function __construct(StorageInterface $storage) {
        $this->_storage = $storage;
    }

    public function read(string $key): array {
        return $this->_storage->read($key);
    }

    public function write(string $key, array $value): bool {
        return ($this->_storage->write($key, $value)) ? true : false;
    }
}

所以现在,每次你需要写/读信息时:

$file = new Storage(new File());
$file->write('filename', ['information'] );
echo $file->read('filename');

$session = new Storage(new Session());
$session->write('filename', ['information'] );
echo $session->read('filename');

在这个例子中,你最终在存储构造函数中使用Duck Typing:

function __construct(StorageInterface $storage) ...

希望能有所帮助;)

我知道我没有给出一个笼统的答案。在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)"

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

Duck typing:

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

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

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

回到编程:

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

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

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

马特·达蒙解释了《心灵捕手》中的鸭子打字

笔录如下:这里有视频链接。

好吧,我们会有问题吗?

克拉克:没问题。我只是希望你能告诉我鸭子打字到底是什么?我的观点是,鸭系没有很好的定义,也不强

WILL: [interrupting] …and neither is strong typing. Of course that's your contention. You're a first year grad student: you just got finished reading some article on duck typing, probably on StackOverflow, and you’re gonna be convinced of that until next month when you get to the Gang of Four, and then you’re gonna be talking about how Google Go and Ocaml are statistically typed languages with structural sub-tying construction. That's going to last until next year, till you're probably gonna be in here regurgitating Matz, talkin’ about, you know, the Pre-Ruby 3.0 utopia and the memory allocating effects of sub-typing on the GC.

克拉克:(吓了一跳)事实上我不会,因为马茨大大低估了——的影响

WILL:“Matz极大地低估了Ruby 3.0的GC对性能的影响。你是从Donald Knuth的《计算机编程艺术》第98页学到的,对吧?是的,我也读过。你打算为我们剽窃整篇文章吗——你对这件事有什么想法吗?或者,这是你的做法吗,你进入堆栈溢出,你读了r/ruby上一些晦涩的段落,然后你假装,你把它当作你自己的——你自己的想法,只是为了取悦一些女孩,让我的朋友难堪?

[克拉克惊呆了]

威尔:像你这样的人最可悲的是,50年后你会开始自己思考,你会发现生活中有三件事是肯定的。第一,不要那样做。第二,如果它走路像鸭子,那它就是鸭子。第三,你花了十五万美元接受本·科西本可以零美分就能得到的教育。

克拉克:是的,但我会有学位,而你会在我们去滑雪的路上,在汽车餐厅给我的孩子们提供一些廉价的html。

威尔:(微笑)也许吧。但至少我不会没有创意。

(打)

威尔:你遇到了(代码的出现)问题?我想我们可以出去谈谈。

克拉克:没问题

一段时间后:

威尔:你喜欢苹果吗?

克拉克说,嗯?

威尔:你觉得这些苹果怎么样?(砰:威尔把一封信重重地贴在窗户上。)我要谷歌的报价!(出示克拉克的录取通知书,上面有他的面试答案:一张鸭子走路、说话、表现得像一只……鹅)。

不必再说

最后。

(这是对旧答案的一个脚注:)

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,所以它只是在编译时尝试,如果一切正常,就没问题。