马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
本帖最后由 heidern0612 于 2019-1-2 10:40 编辑
写心得的过程都是自我思考的过程,借鉴了很多互联网大佬和鱼C论坛前辈的经验,仓促间难免有所疏漏,如有错误,恳请指出,不胜感激。
" When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck."
Duck typing 这个概念来源于美国印第安纳州的诗人詹姆斯·惠特科姆·莱利(James Whitcomb Riley,1849-1916)的诗句。
意大利软件工程师、Python软件基金会研究员Alex Martelli 于2000年左右在Python的邮件组中最早将这个概念引入了程序设计范畴中:
" In other words, don't check whether it IS-a duck: check whether it QUACKS-like-a duck, WALKS-like-a duck, etc, etc, depending on exactly what subset of duck-like behaviour you need to play your language-games with"
(自译:换句话说,不用去管它是不是只鸭子,只要它叫起来像只鸭子,走起来像只鸭子…等等,完全取决于你的“言语”类型要如何去精确的描述“像鸭子一样行为”的子集。)
"鸭子类型"的语言是这么推断的:一只鸟走起来像鸭子、游起泳来像鸭子、叫起来也像鸭子,那它就可以被当做鸭子。也就是说,它不关注对象的类型,而是关注对象具有的行为(方法)。
换句话说,就是方法不判断对象类型。
在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定。
鸭子类型通常得益于不测试方法和函数中参数的类型,而是依赖文档、清晰的代码和测试来确保正确使用。
从静态类型语言转向动态类型语言的用户通常试图添加一些静态的(在运行之前的)类型检查,从而影响了鸭子类型的益处和可伸缩性,并约束了语言的动态特性。
鸭子类型的缺点和不足在于:
"鸭子类型"没有任何静态检查,如类型检查、属性检查、方法签名检查等。
"鸭子类型"语言的程序可能会在运行时因为不具备某种特定的方法而抛出异常。
如果一只小狗(对象)想加入合唱团(以对象会不会嘎嘎嘎叫的方法为检验标准),也学鸭子那么嘎嘎嘎叫,好吧,它加入了,可是加入之后,却不会像鸭子那样走路,那么,迟早要出问题的。
再举个例子:一只小老鼠被猫盯上了,情急之下,它学了狗叫,猫撤了之后,小老鼠的妈妈不无感叹的对它说:看吧,我让你学的这门儿外语多么重要啊。
这虽然是个段子,但是,由于猫在思考时,使用了 "鸭子测试",它以为会叫的就是狗,会对自己产生威胁,所以撤退了,也正是因为这个错误的判断,它误失了一次进食机会。
动态类型语言的面向对象设计中,鸭子类型的概念至关重要。
利用鸭子类型的思想,我们不必借助超类型的帮助,就能轻松地在动态类型语言中实现一个原则:“面向接口编程,而不是面向实现编程”。
例如,一个对象若有push和pop方法,并且这些方法提供了正确的实现,它就可以被当作栈来使用。
一个对象如果有length属性,也可以依照下标来存取属性(最好还要拥有slice和splice等方法),这个对象就可以被当作数组来使用。
python中的鸭子类型允许我们使用任何提供所需方法的对象,而不需要迫使它成为一个子类。
由于python属于动态语言,当你定义了一个基类和基类中的方法,并编写几个继承该基类的子类时,
由于python在定义变量时不指定变量的类型,而是由解释器根据变量内容推断变量类型的(也就是说变量的类型取决于所关联的对象),
这就使得python的多态不像是c++或java中那样,定义一个基类类型变量而隐藏了具体子类的细节。
举例如下:class AudioFile:
def __init__(self, filename):
if not filename.endswith(self.ext):
raise Exception("Invalid file format")
self.filename = filename
class MP3File(AudioFile):
ext = "mp3"
def play(self):
print("Playing {} as mp3".format(self.filename))
class WavFile(AudioFile):
ext = "wav"
def play(self):
print("Playing {} as wav".format(self.filename))
class OggFile(AudioFile):
ext = "ogg"
def play(self):
print("Playing {} as ogg".format(self.filename))
class FlacFile:
"""
Though FlacFile class doesn't inherit AudioFile class,
it also has the same interface as three subclass of AudioFile.
It is called duck typing.
"""
def __init__(self, filename):
if not filename.endswith(".flac"):
raise Exception("Invalid file format")
self.filename = filename
def play(self):
print("Playing {} as flac".format(self.filename))
class FlacFile中"""…"""内容自译如下:
虽然FlacFile类并没有继承自AudioFile类,但它一样和AudioFile的三个子类拥有相同的接口,因此FlacFile被叫做鸭子类型。
上面的代码中,MP3File、WavFile、OggFile三个类型继承了AudioFile这一基类,而FlacFile没有扩展AudioFile,但是可以在python中使用完全相同的接口与之交互。
因为任何提供正确接口的对象都可以在python中交替使用,它减少了多态的一般超类的需求。
继承仍然可以用来共享代码,但是如果所有被共享的都是公共接口,鸭子类型就是所有所需的。
这减少了继承的需要,同时也减少了多重继承的需要;
通常,当多重继承似乎是一个有效方案的时候,我们只需要使用鸭子类型去模拟多个超类之一(定义和那个超类一样的接口和实现)就可以了。
|