• Python中的鸭子类型


    今天,我们来聊一聊Python中的鸭子类型(duck typing)。

    编程语言具有类型概念,例如Python中有数字类型、字符串类型、布尔类型,或者更加复杂的结构,例如元组tuple、列表list、集合set和字典类型dict等等。

    根据如何将类型解析并赋值给各种构造(例如变量,表达式,函数,函数参数等),编程语言可以归类为“鸭子类型”,“结构化类型”或“标称类型”。

    本质上,分类决定了对象如何被解析并推断为具体的类型。

    鸭子测试

    鸭子类型(duck typing)语言使用鸭子测试来评估对象是否可以被解析为特定的类型。Python就是其中一种。

    这个概念的名字来源于由詹姆斯·惠特科姆·莱利提出的鸭子测试。“鸭子测试”可以这样表述:

    “如果看起来像鸭子,叫起来像鸭子,那么它一定是鸭子。”

    If it looks like a duck and quacks like a duck, it must be a duck.——James Whitcomb Riley

    Python



    鸭子类型

    鸭子类型在程序设计中是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合"决定。

    在鸭子类型中,关注点在于对象的行为,能作什么;而不是关注对象所属的类型。例如,在不使用鸭子类型的语言中,我们可以编写一个函数,它接受一个类型为"鸭子"的对象,并调用它的"走"和"叫"方法。在使用鸭子类型的语言中,这样的一个函数可以接受一个任意类型的对象,并调用它的"走"和"叫"方法。如果这些需要被调用的方法不存在,那么将引发一个运行时错误。任何拥有这样的正确的"走"和"叫"方法的对象都可被函数接受的这种行为引出了以上表述,这种决定类型的方式因此得名。

    鸭子类型通常得益于"不"测试方法和函数中参数的类型,而是依赖文档、清晰的代码和测试来确保正确使用。

    在常规类型中,我们能否在一个特定场景中使用某个对象取决于这个对象的类型,而在鸭子类型中,则取决于这个对象是否具有某种属性或者方法——即只要具备特定的属性或方法,能通过鸭子测试,就可以使用。



    Python术语

    Python术语表中的鸭子类型duck-typing

    A programming style which does not look at an object’s type to determine if it has the right interface; instead, the method or attribute is simply called or used (“If it looks like a duck and quacks like a duck, it must be a duck.”) By emphasizing interfaces rather than specific types, well-designed code improves its flexibility by allowing polymorphic substitution. Duck-typing avoids tests using type() or isinstance(). (Note, however, that duck-typing can be complemented with abstract base classes.) Instead, it typically employs hasattr() tests or EAFP programming.

    译文:

    鸭子类型是一种编程风格,决定一个对象是否有正确的接口,关注点在于它的方法或属性,而不是它的类型(“如果它看起来像鸭子,像鸭子一样嘎嘎叫,那么它一定是鸭子。”)。通过强调接口而不是特定类型,设计良好的代码通过多态提高了灵活性。鸭子类型无需使用 type()isinstance() 进行检查(注意,鸭子类型可以用抽象基类来补充),相反,它通常使用 hasattr()来检查,或是 EAFP 编程。



    代码示例

    举个例子:

    # 定义一个计算函数,接收3个入参
    def calculate(a, b, c):
        return (a + b) * c
    
    
    # 分别计算3种情况的结果
    result1 = calculate(1, 2, 3)
    result2 = calculate([1, 2, 3], [4, 5, 6], 2)
    result3 = calculate("打工人", "打工魂", 3)
    
    # 打印3种结果
    print(result1, result2, result3, sep='\n')
    

    代码解释:在上面的例子中,我们每次调用calculate()都使用的是不同的对象(数字、列表和字符串),并且它们在继承关系中没有联系。只要输入对象支持+*方法,操作就能成功。

    执行后输出:

    9
    [1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6]
    打工人打工魂打工人打工魂打工人打工魂
    

    这样,鸭子类型在不使用继承的情况下使用了多态。

    再举个栗子:

    # 鸭子类
    class Duck:
    
        def quack(self):
            print("这鸭子正在嘎嘎叫")
    
        def feathers(self):
            print("这鸭子拥有白色和灰色的羽毛")
    
    # 人类
    class Person:
    
        def quack(self):
            print("这人正在模仿鸭子")
    
        def feathers(self):
            print("这人在地上拿起1根羽毛然后给其他人看")
    
    
    # 函数/接口
    def in_the_forest(duck):
        duck.quack()
        duck.feathers()
    
    
    if __name__ == '__main__':
    
        donald = Duck()  # 创建一个Duck类的实例
        john = Person()  # 创建一个Person类的实例
    
        in_the_forest(donald)  # 调用函数,传入Duck的实例
        in_the_forest(john)    # 调用函数,传入Person的实例
    

    代码运行后输出:

    这鸭子正在嘎嘎叫
    这鸭子拥有白色和灰色的羽毛
    这人正在模仿鸭子
    这人在地上拿起1根羽毛然后给其他人看
    




    总结

    鸭子类型是一个与动态类型(dynamic typing)相关的概念,其中关注的是它定义的方法,而不太关注对象的类型或所属类。当您使用鸭子类型时,你无需做检查类型。相反,您要检查给定方法或属性是否存在。

    鸭子类型语言为程序员提供了最大的灵活性,程序员只需写最少量的代码。但是这些语言可能并不安全,可能会产生运行时错误,使用时需要格外注意。




    参考链接:

    1. Python术语表:https://docs.python.org/3/glossary.html#term-duck-typing
    2. 维基百科:https://zh.wikipedia.org/wiki/鸭子类型
  • 相关阅读:
    Java数据库操作(MySQL与SQLserver)
    LeetCode 11. 盛最多水的容器
    LeetCode 10.正则表达式匹配
    LeetCode 9.回文数
    LeetCode 7. 整数反转
    LeetCode 6.Z 字形变换
    LeetCode 4.寻找两个正序数组的中位数
    LeetCode 3. 无重复字符的最长子串
    JOI2020遗迹
    线性规划对偶
  • 原文地址:https://www.cnblogs.com/amoyshmily/p/16067858.html
Copyright © 2020-2023  润新知