在软件开发中,鸭子类型是什么意思?
在软件开发中,鸭子类型是什么意思?
Quack
。f
需要预先指定其参数必须支持某些方法Quack
。常见的方法是使用接口。interface IQuack {
void Quack();
}
void f(IQuack x) {
x.Quack();
}
f(42)
会失败,但只要donald
是IQuack
子类型的实例,f(donald)
就可以工作。
另一种方法是结构类型——但同样,方法Quack()
在形式上被指定为任何不能证明它quack
的东西都会导致编译器失败。
def f(x : { def Quack() : Unit }) = x.Quack()
f :: Quackable a => a -> IO ()
f = quack
Quackable
类型类确保了我们方法的存在。
def f(x):
x.Quack()
f
得到一个支持 Quack()
的 x
,一切都没问题,否则在运行时会崩溃。template <typename T>
void f(T x) { x.Quack(); }
def f(x)
而不是 def f(IQuack x)
。 - PProteus“如果它走起来像鸭子,叫起来像……等等” - 是的,但那是什么意思?!
我们关注的是“对象”能做什么,而不是它们是什么。
让我们通过一个例子来解释:
请参阅以下详细信息:但是高尔夫球杆怎么能像汽车一样“驾驶”呢?它们不是不同的吗?如果你使用像Ruby这样的语言,我们不关心对象是什么:
class Car
def drive
"I"m driving a Car!"
end
end
class GolfClub
def drive
"I"m driving a golf club!"
end
end
def test_drive(item)
item.drive # don't care what it is, all i care is that it can "drive"
end
car = Car.new
test_drive(car) #=> "I'm driving a Car"
club = GolfClub.new
test_drive(club) #=> "I"m driving a GolfClub"
# welcome to duck typing!
Bird
的对象并调用其 walk()
方法。你可以考虑两种方法:
Bird
类型或代码将无法编译。如果有人想使用我的函数,他们必须知道我只接受 Bird
。对象
,然后只需调用对象的 walk()
方法。因此,如果 object
可以 walk()
,那么就是正确的。如果不能,我的函数将失败。因此,重要的不是对象是 Bird
还是其他任何东西,而是它能够 walk()
(这就是 鸭子类型)。我看到很多答案都在重复那句老话:
如果它看起来像鸭子,叫起来像鸭子,那么它就是鸭子
然后进入对鸭子类型可以做什么的解释,或者是一个似乎更加混淆概念的例子。
我并不觉得这有太大帮助。
以下是我找到的最好的一份简单易懂的关于鸭子类型的解释:
鸭子类型指的是对象是通过其行为定义的,而不是通过其本身的属性或类型定义的。
这意味着我们不太关心一个对象的类/类型,而更关心可以在其上调用哪些方法以及可以对其执行哪些操作。我们不关心它的类型,我们关心它能做什么。
不要成为一个江湖骗子;我支持你:
注意::=
可以读作 "被定义为"。
"鸭子类型" 的意思是: 直接在任何对象上尝试方法(函数调用),而不是首先检查对象的类型以查看该方法是否是这种类型的有效调用。
我们称之为"尝试方法,而不是检查类型" 类型,"方法调用类型检查" 或简称"方法调用类型检查"。
在下面更详细的解释中,我将详细解释这一点,并帮助您理解荒谬、深奥和晦涩难懂的术语"鸭子类型"。
更详细的解释:
Python实现了上述概念。考虑这个例子函数:
def func(a):
a.method1()
a.method2()
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()
duck_or_duck_like_object.swim()
维基百科有一个非常详细的解释:
http://en.wikipedia.org/wiki/Duck_typing
鸭子类型是一种动态类型的风格,其对象的当前方法和属性集决定了有效的语义,而不是它继承于特定类或实现特定接口。
重要的注意点可能是,在使用鸭子类型时,开发人员更关心对象的哪些部分被使用,而不是实际底层类型是什么。
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)"
def append_song(result, song)
result << song.title << " (" << song.artist << ")"
end
result = ""
append_song(result, song) # => "I Got Rhythm (Gene Kelly)"
您无需检查参数类型。如果它们支持<<(在结果的情况下)或标题和艺术家(在歌曲的情况下),一切都会正常工作。如果不支持,您的方法将抛出异常(就像您检查类型时所做的那样)。但是,如果不进行检查,则您的方法变得更加灵活。您可以将数组、字符串、文件或任何其他使用<<添加的对象传递给它,它都能正常工作。
看看这门语言本身可能会有所帮助;对我来说通常很有帮助(我不是以英语为母语的人)。
在鸭子类型中:
1)单词typing并不是指在键盘上打字(这是我一直以来的印象),而是指确定“那个东西是什么类型的?”
2)单词duck表达了如何进行这种确定;它是一种“松散”的确定,就像:“如果它走起路来像只鸭子…那它就是只鸭子”。它是“松散”的,因为这个东西可能是一只鸭子,也可能不是,但它是否真的是一只鸭子并不重要;重要的是我可以像对待鸭子一样对待它,并期望它表现出鸭子的行为。我可以喂它面包屑,这个东西可能会朝我走过来、冲向我或者后退……但它不会像灰熊一样把我吞掉。
以下是翻译文本。视频链接在此处(点击访问).
查基:好吧,我们会有问题吗?
克拉克:没有问题。我只是希望你能给我一些关于鸭子类型实际上是什么的见解。我的观点是,鸭子类型没有很好地定义,强类型也是如此。
威尔:[打断他]…强类型也是如此。当然那是你的观点。你是一年级的研究生: 你刚刚读完了一篇关于鸭子类型的文章,可能是在StackOverflow网站上,而且你会被说服到下个月,直到你到达Gang of Four,然后你会谈论Google Go和Ocaml是具有结构子类型构造的统计类型语言。这将持续到明年,直到你可能在这里背诵Matz的话,谈论Pre-Ruby 3.0时代的乌托邦以及子类型分配对GC的内存分配影响。
克拉克:[吃惊]实际上我不会这么做,因为Matz严重低估了Ruby 3.0的GC对性能的影响。
威尔:"Matz极度低估了Ruby 3.0的GC对性能的影响。你从Donald Knuth的《计算机编程艺术》第98页上得到的,对吧?是啊,我也读过。你要抄袭整个东西给我们看吗?你有自己的想法吗?还是说这就是你的事情,你来到StackOverflow网站,阅读一些关于r/ruby的晦涩段落,然后假装它是你自己的想法,只是为了打动一些女孩,让我的朋友难堪?
[克拉克目瞪口呆]
威尔:看看像你这样的人的悲哀之处在于,大约50年后,你会开始自己思考,并得出这样的结论:人生有三件必然的事情。第一,别那样做。第二,如果它走起路来像只鸭子,那么它就是只鸭子。第三,你花了15万美元去接受教育,但实际上你可以通过Ben Koshy在StackOverflow网站上免费获得答案。
克拉克:是的,但我会拥有学位,而你将通过React在驱动器通道为我的孩子提供便宜的HTML服务,并在去滑雪之旅的路上。
威尔:[微笑]是啊,也许吧。但至少我不会没有创意。
(片刻沉默)
威尔:你有问题3吗?我想我们可以走出去解决问题。
克拉克:没有问题。
一些时间后:
威尔: 你喜欢苹果吗?
克拉克有点懵了。
威尔: 你喜欢这些苹果怎么样?(啪地一声,威尔把一封来自谷歌公司的录取信拍在窗户上。)我收到了谷歌的录取通知!(向克拉克展示录取信,上面显示着他的面试答案:一只行走、说话、表现得像……鹅的鸭子的图片。)
字幕翻译.
(这是对旧回答的注脚:)
3 Advent of Code问题。