910 字
约 3 分钟
3
数据契约为什么重要——先定数据结构,再让模块按它协作

③ 数据契约为什么重要——先定数据结构,再让模块按它协作

目标:get 一个工程习惯——在写一堆模块前,先定义好它们之间传递的数据长得什么样。yt-dlp 靠一个统一的 info dict 让几十个模块无缝协作,就是"数据契约"的典范。


一、没有契约的痛(猜来猜去的下场)

想象 3 个人分头写 3 个模块,各写各的、不回传数据结构:

# 模块A 认为视频信息是 {'title': ..., 'url': ...}
# 模块B 以为视频信息是 {'name': ..., 'link': ...}
# 模块C 又收到 {'t': ..., 'u': ...}

A 把数据交给 B:B 读 info['name']KeyError,崩溃。你甚至不知道哪个字段名才是对的,只能回去翻代码、互相质问。这是团队协作/系统集成里最常见的坑。

二、有契约的爽(先定义好"样子")

先花 5 分钟定死一份"数据格式文档"(契约),让所有模块只认这份格式

Video = {
    'id':      str,      # 必填
    'title':   str,      # 必填
    'formats': list,     # 必填:每种清晰度一个 dict
}

然后每个模块都按这个结构读写,谁也不会少字段、不会用错字段名。对接第三方模块时尤其重要——接口数据协定先于实现

三、数据契约的三个作用

作用 说明
① 解耦 模块之间不用"互相猜对方",只认契约 → 各自独立开发/替换
② 稳定 契约定了,未来换内部实现不影响依赖方(只要产出仍符合契约)
③ 可测 能造一份标准假数据,单独测每个模块

四、必填 vs 可选:契约要分级

好的契约会区分"必须有"和"可以有",这样既有约束又有弹性:

# yt-dlp 的契约分级(info dict)
必填:  id, title, (formats 或 url)      # 缺了就不能干活
可选:  description, thumbnail, duration ...
#         # 缺了也只是"没这个信息",不崩

[!tip] 关键设计:必填字段尽量少,可选字段尽量多。因为现实数据常缺字段,若把什么都设成"缺了就必须报错",系统会很脆弱。yt-dlp 管这叫"致命 vs 非致命提取"。

五、实现层面怎么"强制"这个契约

手段 用途
类型注解 + dataclass / Pydantic 编译/运行期校验字段
抽象基类 / 协议 Protocol 约束"模块必须实现的方法"
文档(docstring / markdown) 人看的规范
单元测试构造标准数据 确保各模块按契约读

Python 典型写法:

from typing import TypedDict

class VideoContract(TypedDict):
    id: str
    title: str
    formats: list[dict]

def process(v: VideoContract) -> ...:
    # 只按 v['id'], v['formats'] 这类约定字段操作

六、与 yt-dlp 的对应

  • 契约文本:yt_dlp/extractor/common.pyInfoExtractor docstring(约 120 行起),定义了 info dict 每个字段。
  • 生命周期文档:学习库 → [[Info-Dict-Contract]]。
  • 一句话:info dict 就是 yt-dlp 全体模块共同签订的"数据契约"

七、一句话带走

在写一堆要协作的模块之前,先把它们之间传的数据结构定死(区分必填/可选),再各自实现。这能避免"字段名猜来猜去"、让模块可独立替换和测试。契约先行,实现后置。

数据契约为什么重要——先定数据结构,再让模块按它协作
http://www.clxhxhhr.top/posts/521/
作者
clxstart
发布于
2026-09-07
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。