示例python的接口类型ducktyping

举报
码乐 发表于 2025/11/27 08:53:08 2025/11/27
【摘要】 1 简介本文实例展示真实存在于 Flask/Django 源码中的经典 duck-typing 实例(已节选并格式化)。这些代码能清晰展示两个框架如何通过鸭子类型实现“行为即接口”的设计。 2 Flaskmake_response() 通过接口类型识别多种返回值Flask 视图函数可以返回 字符串、dict、元组、Response 对象等,都是靠 duck typing 来判断并转换。F...

1 简介

本文实例展示真实存在于 Flask/Django 源码中的经典 duck-typing 实例(已节选并格式化)。

这些代码能清晰展示两个框架如何通过鸭子类型实现“行为即接口”的设计。

2 Flask

  • make_response() 通过接口类型识别多种返回值

Flask 视图函数可以返回 字符串、dict、元组、Response 对象等,都是靠 duck typing 来判断并转换。

Flask 源码片段:flask/app.py 中 make_response()(核心部分)

flask/app.py

      def make_response(self, rv):
          # tuple: (body, status) or (body, headers) or (body, status, headers)
          if isinstance(rv, tuple):
              rv = self._process_response_tuple(rv)

          # Response object
          if isinstance(rv, self.response_class):
              return rv

          # WSGI application
          if callable(rv):
              return self.response_class.from_app(rv, self)

          # dict (will be jsonify in 2.2+)
          if isinstance(rv, dict):
              rv = jsonify(rv)

          # str or bytes
          if isinstance(rv, (str, bytes)):
              return self.response_class(rv)

          # object that knows how to convert itself
          return self.response_class.force_type(rv, request.environ)

Duck typing 体现在如果 rv 是可调用对象(callable),则当作 WSGI app 处理,不关心是不是某个类,只关心“能不能被调用”。

如果它能被转换成 Response,那么调用 force_type() 转换无需继承某个基类。

任何带特定行为的对象能自动工作

  字典 → JSON
  字符串 → Response
  可调用 → WSGI app

核心思想:不检查类型,而检查是否具有特定的行为(callable、可迭代、可转成字符串等)。

3、Flask:FileStorage 被视为文件对象,因为它实现了 read()

Flask 上传文件可像本地文件那样 .read()。

Werkzeug 源码片段(Flask 基于 Werkzeug)werkzeug/datastructures.py 中 FileStorage:

    class FileStorage:
        def read(self, *args, **kwargs):
            return self.stream.read(*args, **kwargs)

只要有 .read(),Flask 中任何接受 file-like object 的代码都能使用它。

完全 duck typing:它不是 file,但“像 file 那样读”,所以被当成 file。

4、Django:中间件只要求可调用对象(callable)

Django 中间件 不要求继承任何基类
只要对象可调用(实现 call),Django 就能把它识别为中间件。

Django 源码片段:django/core/handlers/base.py 中中间件加载

django/core/handlers/base.py

    def load_middleware(self):
        for middleware_path in settings.MIDDLEWARE:
            mw_class = import_string(middleware_path)
            mw_instance = mw_class(get_response)

            #只要实例是可调用的,就直接加入链
            if callable(mw_instance):
                self._middleware_chain = mw_instance

Duck typing 体现在Django 不关心你是否继承某个 Middleware 基类

只检查“是否可调用”

因为可以是函数、类实例、闭包

一个最简中间件:

    class SimpleMiddleware:
        def __init__(self, get_response):
            self.get_response = get_response

        def __call__(self, request):
            # do something
            return self.get_response(request)

只要有 call() 就能用。

4、Django:QuerySet 像列表,因为它实现了迭代协议

Django QuerySet 看起来像列表,但不是列表。它可以:

    被 for 遍历

    被 len() 调用

    被切片

像 list 一样工作

这些都靠 duck typing。

源码片段:django/db/models/query.py 中 QuerySet 的 iter()
django/db/models/query.py

    class QuerySet:
        def __iter__(self):
            self._fetch_all()
            return iter(self._result_cache)

只要实现 iter(),Python 就会把它当作可迭代对象(iteration protocol)。

5、Django Storage:任何有 read/write 的对象都可作为文件

Storage.save() 不要求具体类型,只要求“像文件那样读”。

源码片段:django/core/files/storage.py

    def save(self, name, content, max_length=None):
        # content can be any file-like object, must have read() and chunks()
        if not hasattr(content, 'chunks'):
            content = File(content)

        return self._save(name, content)

Duck typing 体现在Django 不要求上传文件继承 File 类

只检查“content 是否有 read() 或 chunks() 方法”

没有则自动包成 File()

任何能 read() 的东西都能作为 content:

bytesIO

其他类型的 file-like object

自己实现的文件包装类

6 小结

欢迎指正。

  框架  	源码位置    	Duck Typing 用法  		核心思想
  Flask   make_response() 	自动处理多种类型返回值(str、dict、callable)  “能像 Response 一样工作即可”
  Flask   FileStorage.read()  上传文件被当作 file-like   “有 read() 就是文件”
  Django  middleware loader   中间件只要求 callable “能被调用就行”
  Django  QuerySet __iter__   QuerySet 像列表    “能迭代就当作 iterable”
  Django  Storage save()  file-like 对象通过 read()/chunks() 被识别  “能像文件一样读即可” 
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。