实现一个自定义的ducktyping

举报
码乐 发表于 2025/11/28 09:49:30 2025/11/28
【摘要】 1 简介本文实现直接运行的 Python 示例代码,用最少逻辑实现类似 Flask/Django 中最典型的 duck typing 行为。代码全部可独立运行,无需安装 Flask/Django,全部是“简化版模型”。 2 示例 1: make_response 对多种返回值的处理展示 Flask 如何靠 duck typing 接受:字符串、字典、可调用对象、自定义 Response ...

1 简介

本文实现直接运行的 Python 示例代码,用最少逻辑实现类似 Flask/Django 中最典型的 duck typing 行为。

代码全部可独立运行,无需安装 Flask/Django,全部是“简化版模型”。

2 示例 1: make_response 对多种返回值的处理

展示 Flask 如何靠 duck typing 接受:字符串、字典、可调用对象、自定义 Response 类

  • 可运行代码

      class SimpleResponse:
          def __init__(self, data, status=200):
              self.data = data
              self.status = status
    
          def __repr__(self):
              return f"<Response {self.status}: {self.data}>"
    
          @classmethod
          def force_type(cls, obj):
              # 模拟 Flask 的 force_type,尝试把对象强转为 Response
              return cls(str(obj))
    
    
      def make_response(rv):
          # 模仿 Flask:tuple = (body, status)
          if isinstance(rv, tuple):
              body, status = rv
              return SimpleResponse(body, status)
    
          # Response 对象
          if isinstance(rv, SimpleResponse):
              return rv
    
          # 可调用 → 视作 WSGI 应用
          if callable(rv):
              return SimpleResponse(rv())
    
          # dict → 自动转换为 JSON 字符串
          if isinstance(rv, dict):
              return SimpleResponse(f"json:{rv}")
    
          # str → 直接构造响应
          if isinstance(rv, str):
              return SimpleResponse(rv)
    
          # 其他对象 → 强转
          return SimpleResponse.force_type(rv)
    
  • 测试 ----------

print(make_response(“hello”))
print(make_response({“msg”: “ok”}))
print(make_response((“not found”, 404)))
print(make_response(lambda: “callable result”))
print(make_response(12345)) # force_type 转 str

  • 输出示例

<Response 200: hello>
<Response 200: json:{‘msg’: ‘ok’}>
<Response 404: not found>
<Response 200: callable result>
<Response 200: 12345>

Duck typing 关键点

可调用对象就当 WSGI app

dict 就当 JSON 内容

任意对象可通过 force_type() 转换

3 示例 2:复刻 Django 中间件“只要可调用就能用”

Django 中间件核心逻辑是:

只要对象可调用(callable),就能被当作中间件使用。

  • 可运行代码

简化版 Django 中的 middleware 链构建

  def load_middleware(middlewares, get_response):
      chain = get_response
      for mw_class in reversed(middlewares):
          mw_instance = mw_class(chain)
          if not callable(mw_instance):
              raise TypeError("Middleware must be callable")
          chain = mw_instance
      return chain
  • 模拟 request handler

def get_response(request):
return f"Final response: {request}"

  • 两种不同类型的中间件 ---------

      class ClassMiddleware:
          def __init__(self, get_response):
              self.get_response = get_response
    
          def __call__(self, request):
              print("ClassMiddleware processing")
              return self.get_response(request)
    
    
      def function_middleware(get_response):
          # 返回一个可调用对象就行
          def inner(request):
              print("FunctionMiddleware processing")
              return get_response(request)
          return inner
    
  • 测试 ----------

    middlewares = [ClassMiddleware, function_middleware]
    handler = load_middleware(middlewares, get_response)

    print(handler(“REQ”))

输出示例

ClassMiddleware processing
FunctionMiddleware processing
Final response: REQ

  • Duck typing 实现点

中间件可以是类(实现了 call

也可以是函数(返回可调用)

Django 不检查继承结构,只检查“能不能调用”

4 示例 3:复刻 Django QuerySet:

关键点:实现 iterable(像 list 却不是 list)

Django 的 QuerySet 并不是 list,但 QuerySet:

能被 for 遍历 → 因为实现了 iter

能被 len 调用 → 通过 len

能被切片 → getitem

  • 可运行代码

      class MiniQuerySet:
          def __init__(self, data_loader):
              self._data_loader = data_loader
              self._cache = None
    
          def _fetch_all(self):
              if self._cache is None:
                  print("Fetching data from database...")
                  self._cache = list(self._data_loader())
    
          def __iter__(self):
              self._fetch_all()
              return iter(self._cache)
    
          def __len__(self):
              self._fetch_all()
              return len(self._cache)
    
          def __getitem__(self, index):
              self._fetch_all()
              return self._cache[index]
    
  • 测试 ----------

def db_loader():
return [1, 2, 3, 4]

qs = MiniQuerySet(db_loader)

for item in qs:
print(item)

print(“Length:”, len(qs))
print(“First item:”, qs[0])

  • 输出示例

Fetching data from database…
1
2
3
4
Length: 4
First item: 1

  • Duck typing 实现点

QuerySet 实现 iterable protocol → 可 for 遍历

实现 getitem → 可切片

实现 len → 支持 len()
无须继承 list,只需“像 list 一样行为”。

6 小结:

三类 duck typing 行为自定义成功

    行为  		框架  		示例  		Duck typing 核心
    多类型响应自动转换   Flask   make_response 示例    “能当响应用即可”
    全可调用中间件 Django  middleware 示例   “能 call() 就是中间件”
    QuerySet 像列表    Django  iterable 示例 “实现迭代行为就能当集合”
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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