示例python的接口类型ducktyping
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() 被识别 “能像文件一样读即可”
- 点赞
- 收藏
- 关注作者
评论(0)