鱼C论坛

 找回密码
 立即注册
查看: 2063|回复: 0

[学习笔记] 【第042讲心得】【鸭子类型】

[复制链接]
发表于 2019-1-1 19:33:57 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

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被叫做鸭子类型。



上面的代码中,MP3FileWavFileOggFile三个类型继承了AudioFile这一基类,而FlacFile没有扩展AudioFile,但是可以在python中使用完全相同的接口与之交互。

因为任何提供正确接口的对象都可以在python中交替使用,它减少了多态的一般超类的需求。

继承仍然可以用来共享代码,但是如果所有被共享的都是公共接口,鸭子类型就是所有所需的。

这减少了继承的需要,同时也减少了多重继承的需要;

通常,当多重继承似乎是一个有效方案的时候,我们只需要使用鸭子类型去模拟多个超类之一(定义和那个超类一样的接口和实现)就可以了。



评分

参与人数 2荣誉 +10 鱼币 +10 贡献 +6 收起 理由
weimeilanlan + 5 + 5 + 3
不二如是 + 5 + 5 + 3 鱼C有你更精彩^_^

查看全部评分

本帖被以下淘专辑推荐:

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-11-22 08:08

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表