Python 编程 -- 如何理解鸭子类型(Duck Typing)?通过一个例子来理解
鸭子类型(Duck Typing)可以建立两个类之间的同类关系,而不需要使用继承(Inheritance)。这点太重要了。判断两个类是否有属于同一类,鸭子类型做到了“问迹不问心”。“迹”就是方法,“问迹”就是只看这个类是否有另外一个类的方法。而“问心”就是问是否有继承关系。
两个类是否可视为同一类?鸭子类型看的是两个类的方法名是否重合,继承类型看的是继承。(继承,当然会有方法名重合。)鸭子类型本质上放宽了两个类被视为同一类的条件。
下面用一个例子解释鸭子类型:

上面的代码中,我从 Python 自带的 _collections_abc 模块中导入了 Container 类, 它包括一个特殊方法, __contains__。 之后,我定义了 EvenNumber 类,它没有继承任何东西。 EvenNumber 类只有一个方法,叫做 __contains__。

现在我问一个问题:可不可以把 EvenNumber 与 Container 视为同一类呢, is EvenNumber a subclass of Container?答案是可以。 因为 EvenNumber 包含了 Container 中的抽象方法 __contains__。这就是鸭子类型,关键是问你有的方法我有没有,而不是问我有没有继承你。
练习1:猜测定义 EvenNumber 类文件中的带注释 (1)、(2)、(3)、(4)、(5)的语句的输出结果。其中(4)测试 EvenNumber 对象 n 是不是可以看成 Container 对象。(5)测试 EvenNumber 类是不是可以看成 Container 的子类。
练习2:将 EvenNumber 类定义中的 __contains__ 改为 __contain__, 即故意漏掉字母 s,查看带注释 (1)、(2)、(3)、(4)、(5)的语句的输出结果。
要理解鸭子类型的实现机制,必须懂抽象基类(Abstract Base Class)。如果不理解抽象基类中的 __subclasshook__ 特殊方法,则无法理解鸭子类型的实现机制,也无法理解 EvenNumber 定义中带注释 (4)与(5) 的语句的输出结果。
练习3:将上图代码中的 “class EvenNumber:” 改为 “class EvenNumber(Container):”, 即让 EvenNumber 继承 Container。猜测代码中的带注释 (1)、(2)、(3)、(4)、(5)的语句的输出结果。
练习4:将上图代码中的 “class EvenNumber:” 改为 “class EvenNumber(Container):”, 即让 EvenNumber 继承 Container。 将 __contains__ 改为 __contain__, 即故意漏掉字母 s。 测试是否可以生成一个 EvenNumber 对象,n = EvenNumber()。提示:Python 抽象基类规定,任何继承它的子类,必须有抽象基类里面的方法(即@abstractmethod 装饰器下面的方法), 否则无法生成该子类的对象。
更多问题?关注我。