首页 > 编程语言 >说它是谁就是谁—Python语言中的鸭子类型

说它是谁就是谁—Python语言中的鸭子类型

时间:2024-10-30 15:46:23浏览次数:9  
标签:quack fly 语言 Python 对象 鸭子 duck 类型

鸭子类型(Duck Typing)是动态类型语言中的一种类型推断风格,尤其在Python语言中得到了广泛的应用。它的核心思想是:“如果它走起路来像鸭子,叫起来像鸭子,那么它就是鸭子”。这句话的意思是,我们不关心对象的类型是什么,而只关心对象的行为。只要对象具有所需的方法和属性,它就可以被视为具有所需的类型。

鸭子表情包|插画|其他插画|李小障YJ - 原创作品 - 站酷 (ZCOOL)

01 /  原理

一个对象的类型不是由其继承的类或实现的接口来决定的,而是由其行为(即对象的方法集)来决定的。如果一个对象能够响应某个特定类型的请求,那么就可以将其视为该类型的对象。当我们需要判断一个对象是否属于某个类型时,我们不需要查看其类型声明,而是检查它是否具有该类型对象所应该具有的方法。如果对象具有这些方法,并且这些方法的行为符合预期,那么我们就可以将其视为该类型的对象。

02 /  应用场景

  • 迭代器协议:在Python中,任何具有__iter__和__next__方法的对象都可称之为迭代器,但对象本身是什么类型不受限制,可以自定义为任何类。

  • 上下文管理器协议:通过实现__enter__和__exit__方法,任何对象都可以被用作with语句的上下文管理器。

  • 依赖注入:在设计模块化和可重用的代码时,可以通过依赖注入的方式传递对象,只要这些对象实现了所需的接口,而不需要关心它们的具体类型。

03 /  Quick Start

下面是一个简单的Python代码示例,展示了鸭子类型的概念:

class Duck:
    def quack(self):
        print("Quack!")
        
    def fly(self):
        print("Flying...")
​
class Person:
    def quack(self):
        print("I'm quacking like a duck!")
        
    def fly(self):
        print("I can't fly!")
​
class Car:
    def quack(self):
        print("Honk honk!")  # 模拟鸭子的叫声
​
def make_quack(duck_like_object):
    if hasattr(duck_like_object, 'quack') and callable(duck_like_object.quack):
        duck_like_object.quack()
    else:
        print("Error: This object cannot quack!")
​
def make_fly(duck_like_object):
    if hasattr(duck_like_object, 'fly') and callable(duck_like_object.fly):
        duck_like_object.fly()
    else:
        print("Error: This object cannot fly!")
​
# 创建实例
duck = Duck()
person = Person()
car = Car()
​
# 测试代码
make_quack(duck)  # 输出: Quack!
make_quack(person)  # 输出: I'm quacking like a duck!
make_quack(car)    # 输出: Honk honk!
​
make_fly(duck)     # 输出: Flying...
make_fly(person)   # 输出: I can't fly!
make_fly(car)      # 输出: Error: This object cannot fly!

在这个例子中,我们定义了三个类:Duck、Person和Car。Duck和Person类都有quack和fly方法,而Car类只有quack方法,模拟汽车按喇叭的声音。我们定义了两个函数make_quack和make_fly,它们接受一个对象作为参数,并检查这个对象是否有相应的方法。如果有,就调用这个方法;如果没有,就打印一条错误信息。

这个例子展示了鸭子类型的核心思想:我们不关心对象的类型,只关心它是否有我们需要的方法。这样,即使是Car类的对象,只要它有quack方法,就可以被make_quack函数接受。而对于make_fly函数,由于Car类没有fly方法,所以会打印错误信息。这正是鸭子类型的特点:如果它表现得像鸭子,我们就把它当作鸭子对待。

注意事项

  • 运行时错误:由于鸭子类型推迟了类型检查到运行时,某些类型相关的错误只有在代码实际运行时才会被发现。因此,良好的测试变得尤为重要。

  • 代码可读性:对于不熟悉鸭子类型的新手或不熟悉代码库的开发者来说,鸭子类型可能会导致代码可读性降低。因此,适当的注释和文档说明变得更加重要。

  • 缺乏明确的契约:在使用鸭子类型时,没有明确的接口契约,这可能导致开发者依赖于文档或查看源代码来了解对象的行为,这可能会增加理解和维护代码的难度

  • 调试困难:由于没有静态类型检查,当在运行时出现错误时,调试可能会变得更加困难。在编译时无法捕获到类型相关的错误,需要在运行时通过测试和调试来发现

  • 可能的运行时错误:由于鸭子类型允许对象在运行时决定其行为,如果对象的行为不符合预期,可能会导致运行时错误,而不是在编译时捕获到错误。

  • 难以进行静态分析:鸭子类型的灵活性使得在静态分析工具中难以确定对象的确切类型和行为。这可能影响一些工具的有效性,如自动化的代码审查工具或IDE的智能提示

  • 接口的明确性:在设计函数或方法时,应该清晰地定义所需对象的接口(即所需的方法和属性),即使不使用显式的类型声明。这有助于其他开发者理解和使用你的代码

  • 使用类型提示:在Python 3.6及以上版本中,可以使用类型提示来增加代码的可读性和IDE的支持。虽然这不会强制类型检查,但它提供了额外的文档和IDE自动完成功能

  • 结合使用抽象基类(ABCs)和协议(Protocols):对于更复杂的代码库,可以使用抽象基类或Python 3.8及以上版本中的协议来定义对象必须实现的接口,这样可以在保持鸭子类型灵活性的同时,提供更多的类型安全保障

04 / 实战案例

电子商务平台的商品推荐系统。在这个系统中,我们可能有多种不同类型的推荐器,比如基于用户的推荐器、基于物品的推荐器、基于协同过滤的推荐器等。这些推荐器的共同点是它们都能提供一个recommend方法,该方法接受用户ID作为参数,并返回一组推荐商品。

class UserBasedRecommender:
    def recommend(self, user_id):
        # 实现基于用户的推荐逻辑
        pass
​
class ItemBasedRecommender:
    def recommend(self, user_id):
        # 实现基于物品的推荐逻辑
        pass
​
class CollaborativeFilteringRecommender:
    def recommend(self, user_id):
        # 实现协同过滤推荐逻辑
        pass
​
def display_recommendations(recommender, user_id):
    recommendations = recommender.recommend(user_id)
    print(f"Recommendations for user {user_id}: {recommendations}")
​
recommender1 = UserBasedRecommender()
recommender2 = ItemBasedRecommender()
recommender3 = CollaborativeFilteringRecommender()
​
display_recommendations(recommender1, "user123")
display_recommendations(recommender2, "user123")
display_recommendations(recommender3, "user123")

在这个例子中,三个类都没有继承自同一个基类,但它们都有recommend方法。因此,我们可以将它们的对象传递给display_recommendations函数,而不需要关心推荐器的具体类型。这样,我们就可以根据需要灵活地添加或替换推荐器,而不影响其他部分的代码。这种设计提高了代码的灵活性和可扩展性,同时也降低了各个推荐器之间的耦合度。

标签:quack,fly,语言,Python,对象,鸭子,duck,类型
From: https://blog.csdn.net/jtaiykt/article/details/143299834

相关文章

  • Python元类揭秘:掌控类的创造艺术
    元类(metaclass),作为类的构造者,不仅决定类的行为,还塑造其结构,开启了一扇通往高级元编程的大门。本文旨在全面解析Python元类的奥秘:从概念解析,到需求分析,再到定义与实践,最后,通过具体实例展现元类在现实世界应用中的魅力。一、元类:描述类的类在Python中,一切皆对象。类,作为对象......
  • Java编程语言:从入门到精通
    Java是一种广泛使用的高级编程语言,由SunMicrosystems在1995年发布,后来被Oracle公司收购。Java以其“一次编写,到处运行”(WriteOnce,RunAnywhere)的理念而闻名,成为企业级应用开发、Android应用开发和大数据处理等领域的重要工具。二、Java的发展历程1991年:SunMicros......
  • C++编程语言:从基础到高级
    C++是一种静态类型的、编译式的、通用的、大小写敏感的、不规则的编程语言,支持过程化、面向对象和泛型编程。C++由丹麦计算机科学家BjarneStroustrup于1979年在贝尔实验室开始开发,最初作为C语言的扩展,称为“CwithClasses”。1983年,正式更名为C++。C++以其高效、灵活......
  • Python工具箱系列(五十五)
    ​​文字识别文字识别是热门研究方向。目前相对成熟的有:•Google的tesseract项目,它能识别100多种语言•基于机器学习的多个项目,例如百度的paddlehubtesseract使用比较简单,但是配置相对复杂一下。在Ubuntubionic的安装过程如下。aptinstall-ytesseract-ocrtesseract-v#......
  • C语言判断单链表是否相交
    ////CreatedbyAdministratoron2024/10/29.//#ifndefLINK_H#defineLINK_H/***链表的结构体*/typedefstructLink{intelement;structLink*next;}link;#endif//LINK_H////判断单链表是否相交//CreatedbyAdministratoron2024/10/30......
  • Python工具箱系列(五十六)
    抠图抠图是基本需求,最常见的应用场景就是证件照,每次去拍照,都要用个纯色的幕布,而且要求衣服不能太浅。其实背后是有原因的:为了管理部门更准确识别出人像。许多科幻电影也是要求演员在绿幕前表演,后期抠图合成逼真的电影。抠图工具非常多,例如PhotoShop就是抠图利器,可以很神奇地把图......
  • Python工具箱系列(五十七)
    图像分割与人脸识别众所周知图像是由若干有意义的像素组成的,图像分割作为计算机视觉的基础,对具有现有目标和较精确边界的图像进行分割,实现在图像像素级别上的分类任务。图像分割可分为语义分割和实例分割两类,区别如下:语义分割:将图像中每个像素赋予一个类别标签,用不同的颜色来表......
  • 编程语言的绘图库有哪些?
    Python中的Matplotlib库特点与应用场景:Matplotlib是Python中最常用的绘图库之一,它提供了广泛的绘图功能,能够创建各种类型的静态、动态图表,包括折线图、柱状图、饼图、散点图等。适用于数据可视化、科学计算可视化、统计图表制作等众多领域。示例代码-绘制简单折线图:i......
  • python调用grpc请求
    gRPC是一款高性能、开源的RPC框架,支持多种编程语言。Protobuf是gRPC使用的默认序化协议,可以将结构化数据序列化为二进制格式,提高数据传输效率。在Python中使用gRPC调用服务时,通常需要先定义协议缓冲区(ProtocolBuffers)消息类型,这些类型是从.proto文件生成的。当你准备调用一个gRP......
  • 时间序列分析:一种二次指数平滑法构建的纺织生产布料年产量线性预测模型 | 基于SQL语言
    目录0问题描述1 符号规定与基本假设 2模型的分析与建立 3模型的求解【基于SQL语言实现】3.1数据准备3.2问题分析步骤1:计算初始值。步骤2:计算一次平滑值。步骤3:计算二次平滑值 步骤4:计算直线趋势模型的系数 及步骤5:构建线性预测模型进行结果预测3.3结......