910 字
约 3 分钟
4
数据契约为什么重要——先定数据结构,再让模块按它协作
③ 数据契约为什么重要——先定数据结构,再让模块按它协作
目标: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.py的InfoExtractordocstring(约 120 行起),定义了 info dict 每个字段。 - 生命周期文档:学习库 → [[Info-Dict-Contract]]。
- 一句话:info dict 就是 yt-dlp 全体模块共同签订的"数据契约"。
七、一句话带走
在写一堆要协作的模块之前,先把它们之间传的数据结构定死(区分必填/可选),再各自实现。这能避免"字段名猜来猜去"、让模块可独立替换和测试。契约先行,实现后置。
数据契约为什么重要——先定数据结构,再让模块按它协作
http://clxhxhhr.top/posts/521/ 评论
0 条
还没有评论,先写一条吧。