我在网上阅读随机主题时遇到了鸭子打字一词,但并没有完全理解。
什么是“鸭子打字”?
我在网上阅读随机主题时遇到了鸭子打字一词,但并没有完全理解。
什么是“鸭子打字”?
Answers:
鸭子类型 意味着操作不会正式指定其操作数必须满足的要求,而只是使用给定的值进行尝试。
与其他人所说的不同,这不一定与动态语言或继承问题有关。
示例任务:Quack
在对象上调用某些方法。
在不使用鸭子模式的情况下,f
执行此任务的函数必须预先指定其参数必须支持某种方法Quack
。常见的方法是使用接口
interface IQuack {
void Quack();
}
void f(IQuack x) {
x.Quack();
}
调用f(42)
失败,但是f(donald)
只要donald
是IQuack
-subtype 的实例,调用就可以工作。
另一种方法是结构类型化 -但同样,该方法Quack()
被正式指定为任何无法quack
提前证明的内容都会导致编译器失败。
def f(x : { def Quack() : Unit }) = x.Quack()
我们甚至可以写
f :: Quackable a => a -> IO ()
f = quack
在Haskell中,Quackable
类型类可确保我们方法的存在。
好吧,就像我说的那样,鸭子输入系统并没有指定要求,而是尝试一切正常。
因此,Python的动态类型系统始终使用鸭子类型:
def f(x):
x.Quack()
如果f
得到a的x
支持Quack()
,那么一切都很好,否则,它将在运行时崩溃。
但是,鸭子输入根本不意味着动态类型-实际上,有一种非常流行但完全静态的鸭子输入方法,它也没有给出任何要求:
template <typename T>
void f(T x) { x.Quack(); }
该函数不会以任何方式告知x
需要的对象Quack
,因此它只是在编译时尝试,如果一切正常,就可以了。
def f(x)
代替def f(IQuack x)
。
关于问题的语义的讨论相当细微(也是非常学术性的),但这是一般的想法:
鸭打字
(“如果它走路像鸭子,而嘎嘎像鸭子,那就是鸭子。”)- 是的!但是,这是什么意思??!最好通过示例说明:
鸭打字功能示例:
想象一下我有一根魔杖。它具有特殊的力量。如果我挥动魔杖说“开车!” 上车,然后开车!
它可以在其他方面起作用吗?不确定:所以我在卡车上尝试了。哇-它也开车!然后,我在飞机,火车和1 Woods(它们是人们用来“驱动”高尔夫球的一种高尔夫俱乐部)上尝试一下。他们都开车!
但这可以用来制作茶杯吗?错误:KAAAA-BOOOOOOM!效果不是很好。====>茶杯不能开车!h !?
这基本上就是鸭子打字的概念。这是一个先试后买的 系统。如果可行,一切都很好。但是,如果失败了,就像手榴弹仍在您的手中,它将在您的脸上炸毁。
换句话说,我们对对象可以做什么感兴趣,而不是对对象是什么感兴趣。
示例:静态类型语言
如果我们关注的是物体的真正含义,那么我们的魔术将仅适用于预先设定的授权类型(在这种情况下为汽车),但对其他可以行驶的物体(卡车,轻便摩托车,嘟嘟车等)则无效。它不能在卡车上使用,因为我们的魔杖期望它只能在汽车上使用。
换句话说,在这种情况下,魔术棒非常仔细地看着物体是什么(是汽车吗?),而不是物体可以做什么(例如,汽车,卡车等是否可以驾驶)。
驾驶卡车的唯一方法是,如果您能以某种方式让魔术棒同时指望卡车和汽车(也许通过“实现通用接口”)。如果您不知道这意味着什么,请暂时将其忽略。
摘要:钥匙取出
是什么在鸭打字重要的是什么对象其实可以做的,而不是对象是什么是。
考虑到您正在设计一个简单的函数,该函数获取类型的对象Bird
并调用其walk()
方法。您可以想到两种方法:
Bird
,否则它们的代码将无法编译。如果有人想使用我的功能,他必须知道我只接受Bird
sobjects
而我只调用对象的walk()
方法。因此,如果object
可以walk()
,则不能,我的功能将失败。因此,这里的对象是a Bird
或其他任何东西并不重要,它可以是很重要的walk()
(这是鸭子类型)必须考虑到在某些情况下鸭子类型可能有用,例如Python 大量使用了鸭子类型。
维基百科有一个相当详细的解释:
http://en.wikipedia.org/wiki/Duck_typing
鸭子类型是动态类型的一种类型,其中对象的当前方法和属性集确定有效的语义,而不是其从特定类或特定接口的实现继承。
重要的提示可能是,使用鸭子类型时,开发人员会更加关注对象的消耗部分,而不是实际的基础类型。
鸭子打字:
如果它说话像鸭子一样走路,那就是鸭子
这通常被称为诱拐(溯因推理或也称为溯,更明确的定义,我认为):
从C(结论,我们所看到的)和R(规则,我们所知道的),我们接受/决定/假定P(前提,财产),换句话说,是给定的事实
...医学诊断的基础
鸭子:C = 走路,说话,R = 像鸭子,P = 是鸭子
返回编程:
对象o具有方法/属性mp1和接口/类型T 要求/定义mp1
对象o具有方法/属性mp2 和接口/类型T要求/定义mp2
...
因此,只要满足某个mp1 ...的定义,就不仅仅在任何对象上接受mp1 ... ,编译器/运行时也应该可以接受断言o是T类型的
很好,上面的示例就是这种情况吗?鸭子打字本质上根本不是打字吗?还是应该称其为隐式键入?
我知道我没有给出广义的答案。在Ruby中,我们不声明变量或方法的类型-一切都只是某种对象。所以规则是“类不是类型”
在Ruby中,类永远不会(好的,几乎永远不会)是类型。相反,对象的类型更多地取决于该对象可以做什么。在Ruby中,我们将此称为鸭子输入。如果一个物体走路像鸭子一样说话,就像鸭子一样说话,那么口译员会很乐意把它当作鸭子一样对待。
例如,您可能正在编写将歌曲信息添加到字符串的例程。如果您来自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)"
您不需要检查参数的类型。如果他们支持<<(对于结果而言)或标题和艺术家(对于歌曲而言),那么一切将正常进行。如果没有,则您的方法仍然会引发异常(就像检查类型一样)。但是如果没有检查,您的方法会突然变得灵活得多。您可以向其传递一个数组,一个字符串,一个文件或其他任何使用<<附加的对象,它就可以正常工作。
鸭打字不是提示类型!
基本上,为了使用“鸭子类型”,您将不使用特定类型,而是使用更广泛的子类型(不是在讨论继承,当我指的是子类型时,我指的是适合同一配置文件的“事物”)。 。
您可以想象一个存储信息的系统。为了写/读信息,您需要某种存储和信息。
存储类型可以是:文件,数据库,会话等。
该界面将让您知道可用的选项(方法),而不管存储类型如何,这意味着在这一点上什么都没有实现!换句话说,接口对如何存储信息一无所知。
每个存储系统都必须通过实现接口的相同方法来知道接口的存在。
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');
在此示例中,您最终在Storage构造函数中使用Duck Typing:
function __construct(StorageInterface $storage) ...
希望它有所帮助;)
我试图以我的方式理解这句著名的句子:“ Python不在乎对象是否是真正的鸭子。它所关心的只是对象,首先是“嘎嘎”,其次是“像鸭子”。
有一个很好的网站。http://www.voidspace.org.uk/python/articles/duck_typing.shtml#id14
作者指出,鸭子类型可以让您创建自己的类,这些类具有自己的内部数据结构-但可以使用常规Python语法进行访问。