探索动态类型编程:深入理解鸭子类型

前言

最近在读Python相关书籍,看到一个词叫"鸭子类型",觉得很有意思,啥是鸭子类型呢?

鸭子类型

"鸭子类型"(Duck Typing)是动态类型语言中的一个概念,它的名字来源于英文中的一句话:"If it walks like a duck and quacks like a duck, then it is a duck."(如果它像鸭子一样走路,像鸭子一样叫,那么它就是一只鸭子。)

在程序设计中,鸭子类型的含义是:关注对象的行为,而不是对象所属的类型。也就是说,如果一个对象拥有某个方法,我们不关心它是什么类型,只关心它能做什么。

案例:统计文件中元音字母(aeiou)的数量

python 复制代码
def count_vowels(fp):
    """统计某个文件中,包含元音字母(aeiou)的数量"""
    if not hasattr(fp, 'read'):
        raise TypeError('must provide a valid file object')
​
    VOWELS_LETTERS = {'a', 'e', 'i', 'o', 'u'}
    count = 0
    for line in fp:
        for char in line:
            if char.lower() in VOWELS_LETTERS:
                count += 1
    return count

然后,我们通常会这么调用

python 复制代码
with open('demo.txt', 'r') as fp:
    print(count_vowels(fp))

核心代码中,我们只判断了fp对象有没有read方法来确定是否执行。在纯粹的鸭子类型编程风格下,不应该出现任何的isinstance类型判断语句。

鸭子类型只关注对象的行为,对类型不做强制要求,大大提高了代码的灵活性。上面我们也可以这样调用

python 复制代码
from io import StringIO
​
print(count_vowels(StringIO('Hello, world!')))

StringIO是Python的io模块下的一个类,它的功能是在内存中读写str。StringIO提供了一个文件类的接口来操作文本数据。

这里,StringIO('Hello, world!')就像在内存中打开了一个文件,该文件的内容是'Hello, world!',然后你可以使用StringIO对象的各种方法,如read(), write(), seek()等,来操作这个"文件"。

当然,我们也可以自己实现一个类型,实现read方法

python 复制代码
class StringList:
    """用于保存多个字符串的数据类,实现了 read() 和可迭代接口"""
​
    def __init__(self, strings):
        self.strings = strings
​
    def read(self):
        return ''.join(self.strings)
​
    def __iter__(self):
        for s in self.strings:
            yield s
​
print(count_vowels(StringList('Hello, world!')))
​

看看,就是因为count_vowels()函数属于鸭子类型编程风格,StringList类型实现了read接口。

当然这些都是鸭子类型带来的好处

局限

  1. 类型不明确,可能导致运行时错误: 在静态类型语言中,如果试图调用一个对象不存在的方法,会在编译时就抛出错误。但在使用鸭子类型的动态语言中,如果调用的方法不存在,错误只会在运行时出现。这可能导致在开发和调试阶段难以发现问题。
  2. 代码可读性降低: 鸭子类型不关注对象的本质,只关注对象的行为,这可能会使得代码的可读性降低。其他开发者在阅读代码时,可能会对一个函数允许接收什么样的参数,或者一个对象会有哪些行为存在所困扰。
  3. 缺乏有效的文档工具: 由于不明确注明输入和输出的类型,这使得用工具自动生成文档变得困难。因为工具无法通过分析代码来推断出函数或方法可能接受哪些类型的参数。
  4. 无法利用一些语言特性: 一些语言特性,例如Python的类型提示,可以帮助开发者在写代码时发现潜在的错误,但使用鸭子类型会使这些特性的作用大打折扣。
  5. 可能会破坏封装: 有些行为或属性应该是对象私有的,不应该被外界访问。但如果只注重行为,不注重对象的类型,可能会导致调用方访问了不应该访问的行为或属性,从而破坏对象的封装。

最后

在选择是否使用鸭子类型时,需要根据具体的情况和需求来权衡利弊。在简单、灵活的情况下,鸭子类型可以是一种有效的编程方式;但在需要明确接口定义、类型安全性较高的情况下,可能需要考虑其他类型系统或设计模式。

相关推荐
程序员大金4 分钟前
基于SpringBoot+Vue+MySQL的在线学习交流平台
java·vue.js·spring boot·后端·学习·mysql·intellij-idea
shinelord明5 分钟前
【Python】Python知识总结浅析
开发语言·人工智能·python
qq_25183645712 分钟前
基于SpringBoot vue 医院病房信息管理系统设计与实现
vue.js·spring boot·后端
雷神乐乐36 分钟前
Python常用函数
开发语言·python
qq_2518364571 小时前
基于springboot vue3 在线考试系统设计与实现 源码数据库 文档
数据库·spring boot·后端
2401_858120531 小时前
古典舞在线交流平台:SpringBoot设计与实现详解
java·spring boot·后端
赐你岁月如歌1 小时前
如何使用ssm实现基于web的网站的设计与实现+vue
java·后端·ssm
闲宇非鱼1 小时前
微服务到底是技术问题还是管理问题?
java·后端·微服务
model20051 小时前
android + tflite 分类APP开发-1
python·tflite·model maker
感谢地心引力2 小时前
【Qt】Qt安装(2024-10,QT6.7.3,Windows,Qt Creator 、Visual Studio、Pycharm 示例)
c++·windows·python·qt·visual studio