Disjointness of two types implies that neither is a subtype of the other

发布时间:2026/11/9 11:44:11

Disjointness of two types implies that neither is a subtype of the other Disjointness of two types implies that neither is a subtype of the other【免费下载链接】ruffAn extremely fast Python linter and code formatter, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/ru/ruffThis is a regression test for https://github.com/astral-sh/ty/issues/2236.[environment] python-version 3.11from types import FunctionType from ty_extensions import Not, AlwaysTruthy, static_assert from ty_extensions._internal import is_subtype_of, is_disjoint_from class Meta(type): ... class F(metaclassMeta): ... static_assert(not is_subtype_of(tuple[FunctionType, type[F]], Not[tuple[*tuple[AlwaysTruthy, ...], Meta]])) static_assert(not is_subtype_of(Not[tuple[*tuple[AlwaysTruthy, ...], Meta]], tuple[FunctionType, type[F]])) static_assert(is_disjoint_from(tuple[FunctionType, type[F]], Not[tuple[*tuple[AlwaysTruthy, ...], Meta]]))这份测试虽短却同时覆盖了 ty 类型系统中的多个关键特性 - [environment] 中的 python-version 3.11变长元组语法 tuple[*tuple[...]] 依赖 PEP 6463.11 引入测试必须在 3.11 环境下运行 - class Meta(type) 与 class F(metaclassMeta)构造一个元类及其使用者用于考验类型检查器对元类关系的推理 - 三个 static_assert分别验证正向非子类型反向非子类型不相交三个结论且三者必须同时成立。 ## 二、三个断言逐一拆解 ### 2.1 测试中的两个复杂类型 先定义两个被比较的类型 python S tuple[FunctionType, type[F]] # 第一个元组类型 T Not[tuple[*tuple[AlwaysTruthy, ...], Meta]] # 第二个是否定类型 - S tuple[FunctionType, type[F]]一个二元组第一元素是任意函数对象FunctionType第二元素是 type[F]——即类对象 F 及其子类对象的类型 - T Not[tuple[*tuple[AlwaysTruthy, ...], Meta]]Not[...] 表示补集见下文 4.3 节被取反的内部类型是 tuple[*tuple[AlwaysTruthy, ...], Meta]——一个以零个或多个 AlwaysTruthy 元素开头、并以一个 Meta 元素收尾的变长元组。 ### 2.2 三条断言的含义 python static_assert(not is_subtype_of(tuple[FunctionType, type[F]], Not[tuple[*tuple[AlwaysTruthy, ...], Meta]])) static_assert(not is_subtype_of(Not[tuple[*tuple[AlwaysTruthy, ...], Meta]], tuple[FunctionType, type[F]])) static_assert(is_disjoint_from(tuple[FunctionType, type[F]], Not[tuple[*tuple[AlwaysTruthy, ...], Meta]])) | 断言 | 表达 | 验证的性质 | | --- | --- | --- | | 第 1 条 | S 不是 T 的子类型 | 不相交 ⇒ S ⊄ T | | 第 2 条 | T 不是 S 的子类型 | 不相交 ⇒ T ⊄ S | | 第 3 条 | S 与 T 不相交 | 两个类型确实无公共居民 | static_assert 是 [crates/ty_python_semantic/resources/mdtest/ty_extensions.md](https://link.gitcode.com/i/bb395401c0b0718becd1ea659d489e6b) 中描述的测试原语它接收任意表达式若表达式在静态层面已知为真则通过否则产生 static-assert-error 诊断。因此前两条断言写成 not is_subtype_of(...)相当于要求子类型判定必须返回否第三条要求不相交判定必须返回是。三者合起来即回归测试标题所述性质**disjointness 蕴含两个方向上的非子类型关系**。 ## 三、核心类型学原理为什么不相交必然互不为子类型 ### 3.1 不相交性的定义 在 ty 的类型系统中两个类型 S 与 T 不相交当且仅当它们的交集为空等价于 Never Two types S and T are disjoint if they have no overlap; that is, their intersection S T is empty (equivalent to Never). 这段定义直接出自 [crates/ty_python_semantic/resources/mdtest/type_properties/is_disjoint_from.md](https://link.gitcode.com/i/9bf91d1977e570d0ae664a4874ac04f6) 的开篇也是本文回归测试所依赖的公理基础。 ### 3.2 不相交 ⇒ 互不为子类型 该结论可由子类型的定义直接推出 - 若 S ⊆ TS 是 T 的子类型则 S 的所有居民都是 T 的居民S T S 非空 - 若 T ⊆ S同理可得 S T T 非空 - 因此一旦 S T ∅不相交S ⊆ T 与 T ⊆ S 必然都不成立。 回归测试正是针对这一逻辑关系编写只要第 3 条断言不相交成立前两条断言双向非子类型就必须同时成立若类型检查器在子类型或不相交判定上出现偏差测试即失败。issue #2236 即是历史上暴露出的此类判定不一致问题。 ### 3.3 逆命题不成立 需要强调的是该性质是**单向蕴含**不相交一定互不为子类型但互不为子类型并不代表不相交。例如两个独立的普通类 A 与 B无继承关系互不为子类型却可能存在同时继承二者的子类 C因此 A 与 B 并不 disjoint。这一点在 [crates/ty_python_semantic/resources/mdtest/type_properties/is_disjoint_from.md](https://link.gitcode.com/i/9bf91d1977e570d0ae664a4874ac04f6) 的 Class hierarchies 一节有大量测试佐证 python class A: ... class B1(A): ... class B2(A): ... # B1 和 B2 都是 A 的子类故不与 A disjoint static_assert(not is_disjoint_from(A, B1)) static_assert(not is_disjoint_from(A, B2)) # B1 与 B2 也不 disjoint因为可能存在共同的子类 class C(B1, B2): ... static_assert(is_subtype_of(C, B1 B2)) 只有当类被 final 修饰不可再被继承或元类互不相容时类层次才会产生 disjoint 关系。 ## 四、测试中四个关键类型构造的原理 ### 4.1 FunctionType 与 AlwaysTruthy函数对象恒为真 AlwaysTruthy 与 AlwaysFalsy 是 ty 中描述真值性恒真 / 恒假的特殊类型见 [crates/ty_python_semantic/resources/mdtest/ty_extensions.md](https://link.gitcode.com/i/bb395401c0b0718becd1ea659d489e6b) AlwaysTruthy and AlwaysFalsy represent the sets of all possible objects whose truthiness is always truthy or falsy, respectively. Python 中的函数对象永远为真因此 FunctionType 是 AlwaysTruthy 的子类型。这是回归测试中 S 的第一个元素能够落入被取反元组内部类型的原因之一。 ### 4.2 type[F] 与元类 Meta类对象是其元类的实例 元类是类的类。class F(metaclassMeta) 声明 F 的元类是 Meta意味着类对象 F 本身是 Meta 的一个实例。由于元类会被子类继承type[F]F 及其所有子类的类对象类型中的每个居民也都是 Meta 的实例即 python type[F] 是 Meta 的子类型 这正是回归测试中 S 的第二个元素能够与内部元组类型的收尾元素 Meta 匹配的关键。ty 需要有能力穿透元类 → 实例这层关系来完成子类型判定本测试即是对该能力的回归保护。仓库中 [crates/ty_python_semantic/resources/mdtest/metaclass.md](https://link.gitcode.com/i/bf05ee84bd27a97c9c328ec9b81752b6) 与 [crates/ty_python_semantic/resources/mdtest/type_properties/is_disjoint_from.md](https://link.gitcode.com/i/9bf91d1977e570d0ae664a4874ac04f6) 的 Instance types versus type[T] types 一节还提供了更多元类参与 disjoint 判定的用例。 ### 4.3 Not[...]否定类型补集 Not[T] 是 ty_extensions 提供的特殊形式表示 T 的补集。之所以需要它是因为 Python 语言本身无法直接表达交集否定等类型层面的运算。仓库中的说明如下 The ty_extensions module provides the Intersection and Not type constructors (special forms) which allow us to construct these types directly. 在该测试中 python T Not[tuple[*tuple[AlwaysTruthy, ...], Meta]] T 就是所有**不**属于 tuple[*tuple[AlwaysTruthy, ...], Meta] 的类型。 ### 4.4 变长元组 tuple[*tuple[AlwaysTruthy, ...], Meta] tuple[*tuple[AlwaysTruthy, ...], Meta] 是 PEP 646 变长元组零个或多个 AlwaysTruthy 元素紧跟着一个 Meta 元素。即它包含 tuple[Meta]、tuple[AlwaysTruthy, Meta]、tuple[AlwaysTruthy, AlwaysTruthy, Meta]……等所有形状。 综合 4.1 与 4.2S tuple[FunctionType, type[F]] 恰好是 (AlwaysTruthy, Meta) 形状因而 python tuple[FunctionType, type[F]] ⊆ tuple[*tuple[AlwaysTruthy, ...], Meta] 即 S 完全落在被取反的内部类型之中。由于 Not[...] 是内部类型的补集S 与 T Not[...] 的交集必然为空——**这就是两条类型 disjoint 的根本原因**也正是第 3 条断言成立的理论依据。 ## 五、底层实现DisjointnessChecker 与 is_disjoint_from ### 5.1 公开 API 层 is_disjoint_from 与 is_subtype_of 定义在 [crates/ty_vendored/ty_extensions/_internal.pyi](https://link.gitcode.com/i/d05e44a6e1cacb6bcb7ec2c3d788aa7f) 中是面向类型系统测试的内部原语 python def is_subtype_of(ty: TypeForm[object], of: TypeForm[object]) - ConstraintSet: Returns a constraint set that is satisfied when ty is a subtype of of. def is_disjoint_from( type_a: TypeForm[object], type_b: TypeForm[object] ) - ConstraintSet: Returns a constraint set that is satisfied when type_a and type_b are disjoint types. Two types are disjoint if they have no inhabitants in common. 二者的返回类型都是 ConstraintSet约束集由 static_assert 求值后产生 Literal[True] / Literal[False]。注释中的 Two types are disjoint if they have no inhabitants in common 与 3.1 节的文档定义完全一致。 ### 5.2 核心判定逻辑 在 [crates/ty_python_semantic/src/types/relation.rs](https://link.gitcode.com/i/42e1608cdf8f64f57a37dde2154c8f59) 中Type 实现了 is_disjoint_from约第 870 行其语义注释揭示了实现思路 Return true if self other should simplify to Never: if the intersection of the two types could never be inhabited by any possible runtime value. Our implementation of disjointness for non-fully-static types only returns true if the *top materialization* of self has no overlap with the *top materialization* of other. For example, list[int] is disjoint from list[str]: the two types have no overlap. But list[Any] is not disjoint from list[str]: there exists a fully static materialization of list[Any] (list[str]) that is a subtype of list[str]. 这里的两个要点 1. **交集简化为 Never**is_disjoint_from 的判定目标就是self other 是否为空与文档定义互为表里 2. **materialization物化策略**对含 Any 等渐进类型gradual type的类型判定只在其顶层物化无交集时才返回真。list[Any] 与 list[str] 不 disjoint因为 list[Any] 存在一个完全静态的物化 list[str] 与后者重叠。 实现上is_disjoint_from 会调用 when_disjoint_from构造一个 DisjointnessChecker 并注入四类访问器visitor - HasRelationToVisitor关系遍历负责子类型等关系 - IsDisjointVisitor不相交性遍历负责具体类型的 disjoint 规则 - SignatureRelationVisitor签名关系遍历 - ApplyTypeMappingVisitor类型物化/映射。 随后调用 checker.check_type_pair(db, self, other) 完成成对检查最后通过 is_always_satisfied 判断约束集是否恒成立。这条调用链正是第 2.2 节三个断言在执行时真正触达的代码路径。 ## 六、元组不相交性规则的完整图景 回归测试中的 S 是一个元组类型而 ty 对元组何时 disjoint有完整的规则体系集中记录在 [crates/ty_python_semantic/resources/mdtest/type_compendium/tuple.md](https://link.gitcode.com/i/535c3d1247920ee767aa3674203941a7) 的 Disjointness 一节 **规则 1最小长度不兼容的元组必 disjoint** Two tuples with incompatible minimum lengths are always disjoint, regardless of their element types. (The lengths are incompatible if the minimum length of one tuple is larger than the maximum length of the other.) python static_assert(is_disjoint_from(tuple[()], tuple[int])) static_assert(not is_disjoint_from(tuple[()], tuple[int, ...])) static_assert(not is_disjoint_from(tuple[str, ...], tuple[int, ...])) **规则 2对应位置元素 disjoint 则元组 disjoint** A tuple that is required to contain elements P1, P2 is disjoint from a tuple that is required to contain elements Q1, Q2 if either P1 is disjoint from Q1 or if P2 is disjoint from Q2. python final class F1: ... final class F2: ... static_assert(is_disjoint_from(tuple[F1, F2], tuple[F2, F1])) static_assert(not is_disjoint_from(tuple[N1, N2], tuple[N2, N1])) **规则 3变长部分永不导致 disjoint** The variable-length portion of a tuple can never cause the tuples to be disjoint, since all variable-length tuple types contain the empty tuple. python static_assert(not is_disjoint_from(tuple[F1, ...], tuple[F2, ...])) **规则 4元组类型不与任意实例类型 disjoint** 由于元组可被子类化ty 刻意放宽了这一判定同时为了自洽禁止两个不同特化的异构元组出现在同一条 MRO 中 python class C: ... static_assert(not is_disjoint_from(tuple[int, str], C)) class I1(tuple[F1, F2]): ... class I2(tuple[F2, F1]): ... class CommonSubtypeOfTuples(I1, I2): ... # error: [invalid-generic-class] 而 [crates/ty_python_semantic/resources/mdtest/type_properties/is_disjoint_from.md](https://link.gitcode.com/i/9bf91d1977e570d0ae664a4874ac04f6) 的 Tuple types 一节进一步补充了元组与字面量、变长元组之间的 disjoint 用例 python static_assert(is_disjoint_from(tuple[()], TypeOf[object])) static_assert(is_disjoint_from(tuple[None], None)) static_assert(is_disjoint_from(tuple[Literal[1]], tuple[Literal[2]])) static_assert(is_disjoint_from(tuple[Literal[1], Literal[2]], tuple[Literal[1], Literal[3]])) static_assert(not is_disjoint_from(tuple[Literal[1], Literal[2]], tuple[int, ...])) 可以看出本文的回归测试是这条规则链上一个高难度的组合用例它同时混合了元组、否定类型、变长元组、真值性类型与元类专门用于防止某个子规则改动时引入回归。 ## 七、否定类型与交集视角下的 disjoint 从实现角度S 与 Not[U] 的 disjoint 判定等价于检查 S Not[U] ∅。在 [crates/ty_python_semantic/resources/mdtest/type_properties/is_disjoint_from.md](https://link.gitcode.com/i/9bf91d1977e570d0ae664a4874ac04f6) 的 Intersections 一节可以找到同款推理模式的正向与负向用例 python # 一侧是正元素、另一侧是该元素的否定时二者 disjoint static_assert(is_disjoint_from(int, ~int)) static_assert(is_disjoint_from(X ~Literal[1], Literal[1])) # 但父类与子类的否定并不总是 disjoint class Parent: ... class Child(Parent): ... static_assert(not is_disjoint_from(Parent, ~Child)) static_assert(is_disjoint_from(~Parent, Child))【免费下载链接】ruffAn extremely fast Python linter and code formatter, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/ru/ruff创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/11/5 22:53:43

香农公式通俗推导:从AWGN信道容量到QAM与信号完整性工程实践

1. 先把这个公式抄在纸上:C B log₂(1 S/N) 做通信和电路的人,没有一个不认识香农公式。不管是讨论信道容量、规划调制阶数、评估链路预算,还是分析信号完整性对误码率的影响,最后都会被这句话框住: C B log₂(1 …

2026/10/31 0:09:48

Python日常脚本实战:文件处理、Excel自动化与踩坑指南

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

2026/11/9 11:40:13

电工证号格式你知道吗?内部消息告诉你怎么处理过期证件

电工证号格式你知道吗?内部消息告诉你怎么处理过期证件 证书过期了不知道怎么复审延期?很多人一遇到这种情况就手忙脚乱,甚至影响工作。其实,只要掌握了正确的流程和信息,复审延期并不难。特别是对于持证人来说,了解【电工证号格式】和【内部消息】是解…

2026/11/9 11:40:13

电力电缆证是什么证,真实经历告诉你怎么复审延期

电力电缆证是什么证,真实经历告诉你怎么复审延期 证书过期了不知道怎么复审延期,这不是个别现象,很多特种作业人员都遇到过类似情况。作为一名从事电力电缆工作的从业者,我亲身经历过因证书过期而暂停作业的尴尬处境,也了解很多同行因此遭受的损失。今天…

2026/11/9 11:40:13

隆昌哪里办电工证 值不值得考 工地太忙根本没时间复习考试

隆昌哪里办电工证 值不值得考 工地太忙根本没时间复习考试 工地太忙根本没时间复习考试,这几乎是所有建筑工地上电工的共同声音。你可能正面临着高压电工证、低压电工证的考试压力,又担心时间不够,怕考不过,还担心这证到底 值不值得考…

2026/11/9 11:40:13

电工证速记课程怎么选?外省证书能转成电子证书吗

电工证速记课程怎么选?外省证书能转成电子证书吗 之前考的证在外省能不能转过来?这几乎是每个准备考电工证的朋友心里最关心的问题。尤其是那些在外地打工、或者刚从外地回来准备报考的学员,最怕的就是证书“不能转”,白费时间和金钱。而现在的…

2026/11/9 0:37:55

电工证丢失还能补吗?千万别踩坑,丢了证等于失业

电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。…

2026/11/9 0:03:09

建设厅的特种工有哪些含金量高?别交完钱考不过

建设厅的特种工有哪些含金量高?别交完钱考不过 怕考不过白交培训费?别急,先搞清楚建设厅的特种工有哪些含金量高的证,再决定是不是值得投入时间和金钱。 建设厅的特种工证是建筑行业里最基础、也最实用的证书之一,涉及多个作业类别,每种证的 含金量…

2026/11/9 0:03:09

荆门市电工证报名怎么考才靠谱 全国通用证别交冤枉钱

荆门市电工证报名怎么考才靠谱 全国通用证别交冤枉钱 怕考不过白交培训费?荆门市电工证报名前必须知道这些避坑指南。别看网上一堆培训机构广告,真要报个电工证,很多人被坑得一塌糊涂。特别是那些打着“包过”旗号的,最后不是考试不过,就是证不是全国通…

2026/11/9 0:03:09

高空电工作业过程通过率揭秘:怎么选靠谱机构不被坑

高空电工作业过程通过率揭秘:怎么选靠谱机构不被坑 不知道去哪报名怕被中介坑?你不是一个人。现在特种作业证报名渠道五花八门,从线下培训机构到线上代理,真假难辨。尤其是像高空电工作业这类高风险、高门槛的作业类型,证书含金量和考试通过率直接影响你…

2026/11/9 6:30:27

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/7 14:50:12

SSM电子书店源码全解析:结构、原理、部署与排错实战

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

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

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

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