1385 字
约 4 分钟
2
两层选择:先「能不能干」,再「谁更合适」

⑫ 两层选择:先「能不能干」,再「谁更合适」

目标:学一个通用决策框架——当你有多个候选可选时,别写成一堆 if/else,而是分两道关卡挑:第一关卡「能力门控」先筛掉干不了的,第二关卡「偏好打分」再在能干的里面排序选最优。yt-dlp 的网络层用这套从一个请求里挑出最合适的 HTTP 后端。

配套文档 ⑪(路由器 + 可插拔后端)。⑪讲"有哪些插头",这篇讲"总机怎么给请求挑插头"。


一、问题:选型最怕「能不能干的」和「谁更优的」混一起

假设要发一个走 socks 代理 的请求,你有 3 个后端可选:

  • urllib、requests、curl_cffi

你会怎么选?最容易写崩的就是把所有条件搓成一个 if/else

if sock代理 且 urllib 不支持:   ...跳过 urllib
elif 请求要伪装 且 curl_cffi 支持: 选 curl_cffi
elif ...

条件一多,这个判断就写成一团谁都看不懂的乱麻,加一个新后端、加一个新条件,就崩一次。

为什么乱?因为它把两类完全不同的问题混在一起了

  1. 能力问题:这后端能不能干(socks 代理 urllib 根本不支持——干不了就是干不了,不用比)
  2. 优劣问题:能干的里面,哪个更合适/更快/更匹配(伪装请求当然该优先 curl_cffi)

这两件事本该分开判断,却被你挤进同一个 if/else。


二、解法:分「两层」挑

yt-dlp 把它拆成两道独立的关卡:

第一层:能力门控(先筛掉不能干的)

每个后端声明自己支持啥(_SUPPORTED_*),不符合就明确拒绝:

# 后端声明自己的能力
class UrllibHandler:
    _SUPPORTED_PROXY_SCHEMES = ('http', 'https')   # 不支持 socks
# ...别的后端支持 socks...

# 门控:validate() 不满足 → 抛 UnsupportedRequest 跳过
for candidate in candidates:
    if candidate.validate(request):     # 能不能干?
        usable.append(candidate)

走 socks 代理的请求一到,urllib 的 validate() 发现 socks 不在 _SUPPORTED_PROXY_SCHEMES → 直接出局,根本进不了下一轮。

第二层:偏好打分(再在能干的里面选最优)

能干的候选,给它们各自打分,分高的优先

# 每个后端可注册一个"偏好函数"给自己加分
preferences = {rh: sum(pref(rh, request) for pref in preferences_funcs)
               for rh in usable}
best = max(usable, key=lambda rh: preferences[rh])   # 取最高分

伪装请求:curl_cffi 命中"伪装偏好" +1000 分,碾压性胜出。


三、两张表格看清「两层」各管什么

关卡 问的问题 输出 例子
① 能力门控 你能不能干? 通过 / 出局 urllib 不支持 socks,出局
② 偏好打分 能干的里面谁最合适? 排序 curl_cffi 支持伪装,+1000 分胜出

[!important] 记住分界:「干不了」用能力拒绝,「干得好」用打分排序。两个机制别混。


四、为什么分开好处大(3 个)

# 好处 具体
1 判断不缠在一起 能力是硬性二值,打分是软性排序,各管各的,不写成乱 if
2 好扩展 加一种能力→ 声明 _SUPPORTED_*;加重某个偏好 → 加个打分函数。互不干扰
3 出错知道找谁 请求没人接(NoSupportingHandlers),一眼看出是"能力层全被筛掉"还是"没人打分"

五、在哪用得上(这个思维极通用)

你的场景 第一层(能不能) 第二层(谁更优)
HTTP 后端 该后端是否支持此协议/代理 伪装/速度/匹配度打分
租户/房产推荐 预算内有没有房 距工作地、租金性价比排序
支付渠道选路 该渠道支持此币种/地区吗 手续费/成功率打分
招聘筛选 资格是否符合硬条件 经验/技能匹配度排序
路由 / 负载均衡 节点健康吗 当前负载/延迟打分

通用规律:只要在「一堆候选中挑一个」,都先用硬性条件筛掉不能干的,再用软性偏好在里面排序——这两步分开,逻辑就清爽。


六、核心金句

选型别写成一坨 if/else。 都分两步:先做"能力门控"(干不了的明确拒绝),再做"偏好打分"(能干的里面按分数取最优)。把"能不能"和"好不好"拆开,系统才清晰、好扩展、好排查。


七、在 yt-dlp 里对应哪里(供回溯)

  • 能力门控:RequestHandler.validate() + _SUPPORTED_URL_SCHEMES / _SUPPORTED_PROXY_SCHEMES / _SUPPORTED_FEATURES
  • 偏好打分:@register_preference + RequestDirector._get_handlers()yt_dlp/networking/common.py
  • 伪装偏好 +1000:yt_dlp/networking/impersonate.py
  • 学习库 → [[Networking-Layer]]

八、一句话带走

当你要在多个候选中挑一个时,别把条件全塞进一个 if/else。学会两层选择第一层"能力门控"硬性筛掉干不了的,第二层"偏好打分"软性在剩下的里面挑最优。以后做路由、选型、推荐、调度,都用这两道关卡——"能不能"和"好不好"永远分开算

两层选择:先「能不能干」,再「谁更合适」
http://www.clxhxhhr.top/posts/534/
作者
clxstart
发布于
2026-09-08
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。