Ruff ty 类型检查器中的泛型内建类型:未绑定继承方法与可变关键字参数的类型推断

发布时间:2026/9/10 3:21:18

Ruff ty 类型检查器中的泛型内建类型:未绑定继承方法与可变关键字参数的类型推断 Ruff ty 类型检查器中的泛型内建类型未绑定继承方法与可变关键字参数的类型推断【免费下载链接】ruffAn extremely fast Python linter and code formatter, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/ru/ruff泛型内建类型generic builtins是类型推断中既基础又容易出错的环节。本文以 Ruff 类型检查器 ty 的测试文档 crates/ty_python_semantic/resources/mdtest/generics/builtins.md 为主体讲解两类核心场景list、dict等泛型内建类型通过继承获得的未绑定方法如list.clear为何无需提供类型参数即可调用以及自定义 typeshed 中dict若不按泛型类声明会导致**kwargs被推断为dict[Unknown, Unknown, Unknown]的惊讶结果。读完本文你将掌握 mdtest 测试格式、reveal_type断言语法以及泛型内建类型声明对推断结果的直接影响。一、背景mdtest 与 Generic builtins 测试文档1.1 mdtest以 Markdown 为载体的类型推断测试在 Ruff 仓库中ty 类型检查器的类型推断与类型检查测试大量使用mdtest格式Markdown 文件中的代码块py、pyi、ipynb、toml被解析为可执行的测试用例行内注释如# revealed:、# error:作为断言与类型检查器输出的诊断结果逐行比对。仓库中 crates/ty_python_semantic/resources/README.md 明确说明Markdown files within themdtest/subdirectory are tests of type inference and type checking; executed by thetests/mdtest.rsintegration test.即crates/ty_python_semantic/resources/mdtest/目录下的所有 Markdown 文件都是类型推断与类型检查的测试由crates/ty_python_semantic/tests/mdtest.rs集成测试执行。这些测试会被打包进 crate 的resources/mdtest/目录随测试运行加载。从 crates/mdtest/src/parser.rs 的解析逻辑可以进一步确认测试格式的细节语言为toml且没有显式文件路径的代码块被视作该测试用例的配置块process_config_block内容以 TOML 反序列化后存入当前节Sectionpy/python/pyi/ipynb代码块作为可检查的嵌入式文件checkable embedded file文件名会自动生成如mdtest_snippet.py、mdtest_snippet.pyi代码块支持显式路径语法例如/typeshed/stdlib/builtins.pyi:前缀用于创建指定路径的测试文件。1.2 本测试文档的位置与主题本文要讲解的 builtins.md 位于generics/目录下与 scoping.md、set_theoretic.md 以及pep695/、legacy/两个子目录共同构成泛型generics主题的测试矩阵。文档标题为 Generic builtins聚焦两个具体场景下面逐一展开。二、未绑定的继承方法list.clear 与 dict.clear2.1 原文档示例原文档的第一节 Unbound inherited methods 给出了如下示例def clear_containers(items: list[int], mapping: dict[str, int]) - None: list.clear(items) dict.clear(mapping)该代码块没有任何断言注释因此这是一个应当通过类型检查而不产生任何诊断的用例。它验证的核心行为是在 typeshed 中list从MutableSequence继承clear方法dict从MutableMapping继承clear方法我们可以直接通过list和dict调用这些继承来的方法而无需提供类型参数即不必写成list[int].clear(items)之类。2.2 为什么未绑定是个值得专门测试的点这里的未绑定有两层含义方法未绑定到实例list.clear(items)是直接通过类而不是实例调用属于未绑定方法调用需要显式传入第一个参数即被操作的容器本身。items: list[int]作为实参传入后类型检查器需要把list的泛型参数与items的类型对齐。类型参数未显式指定MutableSequence本身是泛型通常带有Self或元素类型参数clear方法的签名中可能包含对Self的引用。调用list.clear(items)时类型检查器必须正确解析这些泛型方法签名中出现的未绑定类型变量unbound typevar在不提供任何类型参数的情况下完成推断。这一行为在实现层面并非理所当然。从 crates/ty_python_semantic/src/types/generics.rs 的源码结构可以看到ty 对类型变量的绑定与解析有一套专门的逻辑resolve_unbound_typevar约 generics.rs 第 74 行负责绑定一个未绑定的类型变量——当表达式解析到一个类型变量而它尚未被任何包围作用域绑定时会进入该路径处理is_visible_across_class_boundary约 generics.rs 第 148 行判断一个绑定在跨越嵌套类边界后是否仍然可见这与从基类继承来的泛型上下文密切相关with_inherited_generic_context约 generics.rs 第 731 行将函数签名与继承自基类的泛型上下文合并。可以推断list.clear(items)之所以能通过检查正是依赖上述继承泛型上下文的解析机制clear的签名来自基类MutableSequence其Self类型变量在调用点通过items: list[int]被正确绑定到具体的list[int]因此无需显式类型参数。2.3 与 PEP 695 / legacy 泛型声明的对照同类目录下的 generics/pep695/classes.md 与generics/legacy/classes.md覆盖了用户自定义泛型类的继承场景其中反复出现通过继承另一个泛型类并使泛型参数部分或全部特化的用例以及# revealed:断言。与本节内容合在一起看可以形成一条完整的脉络无论是内建类型的基类MutableSequence、MutableMapping还是用户自定义泛型类的基类类型检查器都必须维护好继承而来的泛型上下文才能在方法调用时不要求调用方重复提供类型参数。三、可变关键字参数与自定义 dict 的泛型声明3.1 问题背景原文档的第二节 Variadic keyword arguments with a customdict 讨论了一个容易被忽略的坑当你在自定义 typeshed 中定义dict时必须像真实 typeshed 一样把它定义为泛型类否则可变关键字参数**kwargs的类型推断结果会出人意料。3.2 测试环境配置该测试用例首先通过 TOML 配置块指定使用自定义 typeshed 作为环境[environment] typeshed /typeshed这意味着类型检查器在该测试中将读取位于/typeshed的自定义类型存根而不是内置的 vendored typeshed。这与真实项目中使用自定义 stubs 的场景例如为私有类型或自定义运行时编写存根一致。3.3 自定义 typeshed 中的泛型 dict测试随后通过显式路径语法声明了两个存根文件。/typeshed/stdlib/builtins.pyiclass object: ... class int: ... class tuple: ... class dict[K, V, Extra]: .../typeshed/stdlib/typing_extensions.pyidef reveal_type(obj, /): ...注意这里的dict被声明为三个类型参数的泛型类dict[K, V, Extra]即键类型K、值类型V以及一个额外的类型参数Extra。这正是与真实 typeshed 保持一致的关键——真实的dict在 typeshed 中同样是带多个类型参数的泛型类而**kwargs在类型检查器内部正是通过dict类型来表达的其类型参数会直接反映在推断结果中。从 crates/mdtest/src/parser.rs 的解析器实现看显式路径语法在代码块上方用路径:标记正是为了在测试中构造这种多文件、自定义路径的虚拟环境而reveal_type作为测试辅助函数在自定义的typing_extensions.pyi中声明说明 mdtest 允许测试自定义辅助 API。3.4 错误声明会带来什么后果原文档紧接着给出了对照性的断言如果dict没有按泛型类声明例如被定义为普通非泛型类那么推断**kwargs的类型时类型检查器无法获知键、值以及额外维度分别对应什么类型结果将是def f(**kwargs): reveal_type(kwargs) # revealed: dict[Unknown, Unknown, Unknown] def g(**kwargs: int): reveal_type(kwargs) # revealed: dict[Unknown, Unknown, Unknown]这里有三个值得注意的细节# revealed:是行内断言reveal_type(kwargs)的推断结果被以注释形式写在下一行格式为# revealed: 推断出的类型。mdtest 运行时会将其与类型检查器实际输出的reveal_type结果比对不一致即测试失败。这与本仓库中其他 mdtest 用例的断言风格完全一致参见 generics/pep695/classes.md 中的# revealed: ty_extensions._internal.GenericContext[...]等写法。即使标注了**kwargs: int也是Unknown第二个函数g显式声明了关键字参数的值类型为int但推断结果依然是dict[Unknown, Unknown, Unknown]。这说明当dict不是正确声明的泛型类时类型检查器对**kwargs的整体类型表达都退化为Unknown——声明值类型并不能补救dict本身缺少类型参数的问题。这正是原文档所说的surprising results。dict[K, V, Extra]与dict[Unknown, Unknown, Unknown]一一对应对比 3.3 节的自定义声明可以看到三个类型参数恰好对应推断结果中的三个槽位。可以推断ty 在推断**kwargs时会将收集到的字典表示为dict类型的实例并分别填充键类型、值类型与额外维度的类型参数只要dict不是泛型类这三个槽位就无法被实例化只能以Unknown兜底。3.5 从源码看 kwargs 的推断路径在 crates/ty_python_semantic/src/types/generics.rs 以及types/目录如 types.rs、types/instance.rs中存在大量围绕类型变量绑定、泛型实例化与reveal_type相关的实现且多处测试用例覆盖**kwargs场景在src/types/infer/、src/types/function.rs等文件中都能检索到相关断言。结合本节文档可以确认一条实现层面的原则**kwargs的类型推断依赖dict作为泛型类的正确声明类型参数的数量与含义直接决定推断结果的形状。这也解释了为什么文档要专门强调必须以与真实 typeshed 相同的方式将其定义为泛型类。四、如何在本地运行这些 mdtest 用例4.1 通过 cargo 运行该测试套件由ty_python_semanticcrate 的集成测试承载标准运行方式是cargo test -p ty_python_semantic --test mdtest当某个测试失败时crates/mdtest/src/lib.rs 中的run函数会在失败信息末尾给出精确重跑该用例的命令其格式为MDTEST_TEST_FILTER完整测试名 cargo test -p ty_python_semantic --test mdtest -- mdtest其中MDTEST_TEST_FILTER是 crates/mdtest/src/lib.rs 中定义的环境变量第 23 行测试名由 Markdown 标题层级拼接而成如Generic builtins - Unbound inherited methods。4.2 常用环境变量crates/mdtest/src/lib.rs 定义了三个与运行行为相关的环境变量环境变量作用MDTEST_TEST_FILTER只运行名称包含该过滤串的测试MDTEST_UPDATE_SNAPSHOTS设为非0时自动更新行内快照snapshot代码块内容MDTEST_GITHUB_ANNOTATIONS_FORMAT设置后以 GitHub Actions annotation 格式输出错误信息4.3 使用 mdtest.py 进入监听模式仓库还提供了 Python 脚本 crates/ty_python_semantic/mdtest.py它以 watch 模式持续监听 Rust 源码、vendored typeshed 与resources/mdtest/目录的变化改动后自动重新编译并运行相关测试。其命令行参数包括位置参数filters部分路径过滤例如generics/builtins.md或builtins.md脚本内部会自动去掉.md后缀匹配--enable-external/-e启用依赖外部资源的测试--no-lockfile-upgrades禁止在 Markdown 测试的依赖要求变化时自动升级 lockfile--no-snapshot-updates禁止自动更新过期的行内快照。默认情况下过期的snapshot代码块会被自动更新lockfile 也会在依赖变化时升级因此开发测试用例时通常无需手动维护这两类文件。五、延伸泛型测试矩阵与阅读建议generics/目录下除本文档外还按语法风格分为两个子目录构成完整的泛型能力测试矩阵generics/pep695/PEP 695 语法class C[T]、def f[T]等内含 classes.md、functions.md、typevartuple.md、paramspec.md 等generics/legacy/PEP 484 传统语法TypeVar、Generic[T]等同样覆盖 classes、functions、typevartuple、paramspec、variance 等主题。对泛型内建类型感兴趣的读者建议按以下顺序阅读本文档builtins.md先理解内建类型继承方法与dict泛型声明的语义pep695/classes.md理解用户自定义泛型类中继承未特化/部分特化的基类如何影响泛型上下文legacy/目录下对应文件对照传统Generic[T]语法下相同的语义是否保持一致。这三者共同回答了同一个问题当一个方法或类型源自泛型基类时调用方需要或不需要显式提供多少类型信息——而本文档正是从内建类型这个最常用的切面出发把答案固定成了可自动验证的测试用例。结语通过 builtins.md 这份测试文档我们看到了 ty 类型检查器在泛型内建类型上的两个明确契约其一list.clear、dict.clear这类继承自泛型基类MutableSequence、MutableMapping的方法调用时无需提供类型参数类型检查器会依据实参自动完成类型变量的绑定其二dict必须在 typeshed 中保持泛型类声明dict[K, V, Extra]否则**kwargs的推断结果会退化为dict[Unknown, Unknown, Unknown]。这两条契约都通过 mdtest 以可复现、可回归的方式固化在仓库中既服务了类型检查器自身的正确性也为使用者编写自定义 typeshed 时提供了直接的实践参照——定义内建类型存根时泛型参数的声明方式会真实地改变类型推断的输出。【免费下载链接】ruffAn extremely fast Python linter and code formatter, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/ru/ruff创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/10 3:16:18

EFI/ESP分区详解:从UEFI启动原理到引导修复实战

玩电脑的人迟早会跟EFI/ESP分区打交道:给新硬盘装Windows装完不开机,磁盘管理里那个100多MB的FAT32小分区想删又删不掉,或者开机直接卡在“EFI Network”界面几十秒不动——这些麻烦背后几乎都是同一件事:EFI系统分区(…

2026/9/10 6:21:36

研发驱动AI落地:制造业智能转型的四步炼金术

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/10 6:21:36

4.5小时攻克单片机原理考试:聚焦51系列核心考点

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/10 6:21:36

全离散扩散建模:Intern Lumina U2 的 token 级生成范式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/10 6:21:36

鸿蒙版Flutter天气应用:Staggered Grid多城市卡片墙实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/10 6:16:35

CANN/GE单算子执行接口

aclopExecuteV2 【免费下载链接】ge GE(Graph Engine)是面向昇腾的图编译器和执行器,提供了计算图优化、多流并行、内存复用和模型下沉等技术手段,加速模型执行效率,减少模型内存占用。 GE 提供对 PyTorch、TensorFlow…

2026/9/9 13:11:35

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/8 7:15:15

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/9 16:31:09

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/10 0:00:55

目录对比去重实战:用哈希算法精准清理重复文件

我电脑里现在还有一块换了三次机的“数据墓地”硬盘,里面存着2016年以前所有旧笔记本的完整备份。平时不觉得有什么,直到前阵子想把它整理归档,发现同一个安装包、同一批照片、同一份论文草稿,在几个不同的备份目录里反复出现。更…

2026/9/10 0:00:55

Leaflet离线地图完整Demo合集:内网部署与坐标纠偏实战

简介:这是一份面向Web GIS开发者的LeafLet离线地图示例合集,帮助开发者快速掌握离线地图从搭建到交互的完整流程。压缩包共723个文件,大小14.06MB,以319个js脚本、175个html页面和29个css样式文件为主体,配合png/svg图…

2026/9/10 0:00:55

MATLAB读取Rinex 3.02观测文件:多系统GNSS数据解析实战

简介:基于MATLAB开发的Rinex3.02版观测文件(o文件)读取代码包,面向卫星定位导航方向的学习者与研究人员,用于解决新版观测文件的数据解析、历元提取与时间转换问题。压缩包共4个文件,包含两个m脚本、一个19…

2026/9/7 16:23:03

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/7 22:46:00

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/9 10:21:54

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码