深入 ty 类型系统:Ruff 类型检查器 Type Compendium(类型事实表)全景解读
2026/9/12 13:03:22 网站建设 项目流程

深入 ty 类型系统:Ruff 类型检查器 Type Compendium(类型事实表)全景解读

【免费下载链接】ruffAn extremely fast Python linter and code formatter, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/ru/ruff

导读

本文以 Ruff 仓库内 ty 类型检查器的type_compendium(类型事实表)为核心,系统讲解 ty 对 Python 类型系统中重要、有趣且特殊的类型——NeverobjectNone、整数LiteraltupleAnyAlwaysTruthy/AlwaysFalsy以及补类型~T——的建模规则与判定逻辑。阅读本文后,你将掌握 ty 在子类型、可赋值性、不相交性、并集/交集归约、类型收窄、真值性等核心关系上的精确语义,并能理解这些事实表文档本身如何作为可运行的 mdtest 测试来约束实现与规范保持一致。

Type Compendium 是什么:一份"活的"类型系统参考手册

type_compendium位于 crates/ty_python_semantic/resources/mdtest/type_compendium/README.md,其中包含一系列关于"重要、有趣且特殊"的类型的"事实表"(fact sheets)。它承担两个职责:

  1. 教育性参考:面向 ty 的开发者与用户,以易于理解的方式说明 ty 对 Python 类型系统各类型的解释;
  2. 活文档(living document):这些文档同时是 mdtest 测试——文档中的每一段 Python 代码都会被真实执行与校验,确保 ty 对类型及其属性的实现与规范保持一致。一旦实现行为与文档断言不符,测试即失败。

从仓库结构看,mdtest 机制由 crates/mdtest 与 crates/ruff_mdtest 提供支持,因此这些文档不只是"读"的,更是"跑"的。

事实表目录(对应仓库文件):

  • Never
  • object
  • None
  • 整数Literal
  • tuple类型
  • Any
  • AlwaysTruthyAlwaysFalsy
  • ~T

文档中大量使用两个关键验证手段:static_assert(来自ty_extensions,在编译期断言某类型关系成立)以及reveal_type/assert_type(揭示或断言某个表达式在 ty 眼中的静态类型)。这些断言就是测试用例本身。

Never:空集与底类型

Never是每个类型的子类型

Never表示值的空集,是 Python 类型系统中的底类型(bottom type):它是每个类型的子类型,但除Never自身以及上界为Never的类型变量外,没有任何类型是Never的子类型。见 never.md:

from ty_extensions import static_assert from ty_extensions._internal import is_subtype_of from typing_extensions import Never, TypeVar class C: ... static_assert(is_subtype_of(Never, int)) static_assert(is_subtype_of(Never, object)) static_assert(is_subtype_of(Never, C)) static_assert(is_subtype_of(Never, Never)) static_assert(not is_subtype_of(int, Never)) T = TypeVar("T", bound=Never) def _(t: T): static_assert(is_subtype_of(T, Never))

Never可赋值给每个类型

Never可赋值给任何类型,这一事实在"需要特定类型值的上下文中调用错误处理函数"时非常有用。例如把raise_error的返回类型从Never改成None会导致类型错误:

from ty_extensions import static_assert from ty_extensions._internal import is_assignable_to from typing_extensions import Never, Any static_assert(is_assignable_to(Never, int)) static_assert(is_assignable_to(Never, object)) static_assert(is_assignable_to(Never, Any)) static_assert(is_assignable_to(Never, Never)) def raise_error() -> Never: raise Exception("...") def f(divisor: int) -> None: x: float = (1 / divisor) if divisor != 0 else raise_error()

Never在注解中的两种用法

返回值位置:表示函数永不返回控制权——总是抛异常、调用sys.exit()、进入死循环或无限递归:

from typing_extensions import Never def raises_unconditionally() -> Never: raise Exception("This function always raises an exception") def exits_unconditionally() -> Never: import sys return sys.exit(1) def loops_forever() -> Never: while True: pass def recursive_never() -> Never: return recursive_never()

参数位置:表示该函数永远不可能被调用,因为永远无法传入一个Never类型的值(这样的值不存在):

def can_not_be_called(n: Never) -> int: return 0

与其他类型的关系:不相交、并集与交集

两个类型不相交(disjoint)当且仅当它们的交集为空。由于Never没有成员,它与所有类型都不相交(包括自身)。同时:

  • 并集Never总是可以从并集中移除,即P | Never | Q | None等价于P | Q | None
  • 交集:与Never求交集结果恒为Never,即P & Never & Q等价于Never
from ty_extensions import static_assert from ty_extensions._internal import is_equivalent_to from typing_extensions import Never class P: ... class Q: ... static_assert(is_equivalent_to(P | Never | Q | None, P | Q | None)) static_assert(is_equivalent_to(P & Never & Q, Never))

Neverobject的互补性

object描述所有可能值的集合,Never描述空集,二者互为补集:~object等价于Never~Never等价于object。这种对偶性贯穿始终:

关系Neverobject
子类型是每个类型的子类型是每个类型的超类型
可赋值性可赋值给每个类型可由每个类型赋值
不相交性与每个类型不相交与每个类型重叠
并集构建并集是 no-opobject求并集得object
交集求交集得Never求交集是 no-op

list[Never]与含Never的元组

list[Never]是一个合理的类型,并不等价于Never——空列表即为其成员:x: list[Never] = []

tuple[int, Never]同样区别于Never:由于元组注解可以描述用户自定义子类,其元素类型始终保留在类型中。同质元组tuple[Never, ...]也与Never不同,因为空元组是它的成员:

static_assert(not is_equivalent_to(tuple[int, Never], Never)) static_assert(not is_equivalent_to(tuple[Never, ...], Never)) t: tuple[Never, ...] = ()

NoReturnNever相同

NoReturn只是Never的另一个名字:

from ty_extensions import static_assert from ty_extensions._internal import is_equivalent_to from typing_extensions import NoReturn, Never static_assert(is_equivalent_to(NoReturn, Never))

object:顶类型

object表示所有 Python 对象的集合,是类型系统中的顶类型(top type)。见 object.md。

超类型与可赋值性

所有类型都是object的子类型,一切值也都可以赋给object。这可以用来构造异构对象集合(但也会抹掉更具体的类型信息):

from ty_extensions import static_assert from ty_extensions._internal import is_subtype_of, is_assignable_to from typing_extensions import Any, Never static_assert(is_subtype_of(int, object)) static_assert(is_subtype_of(str, object)) static_assert(is_subtype_of(type, object)) static_assert(is_assignable_to(int, object)) static_assert(is_assignable_to(str | bytes, object)) static_assert(is_assignable_to(Never, object)) static_assert(is_assignable_to(Any, object)) x: list[object] = [1, "a", ()]

重叠性、并集与交集

Never外,没有哪个类型与object不相交;intstrtypeAny都与object重叠,只有Neverobject不相交。

  • object求并集:结果恒为object,即int | object | None等价于object
  • object求交集:结果等价于原类型,即P & object & Q等价于P & Q

None:单例类型

None(或NoneType,见下文)是只有一个成员——对象None——的单例类型。见 none.md。

单例类型的子类型

与其他单例类型一样,None仅有的子类型是None自身与Never

static_assert(is_subtype_of(None, None)) static_assert(is_subtype_of(Never, None))

Optional[T]只是T | None的别名

from typing import Optional, Union from ty_extensions import static_assert from ty_extensions._internal import is_equivalent_to class T: ... static_assert(is_equivalent_to(Optional[T], T | None)) static_assert(is_equivalent_to(Optional[T], Union[T, None]))

基于is/is not的类型收窄

与其他单例类型一样,ty 支持通过is None/is not None进行类型收窄:

from typing_extensions import assert_type class T: ... def f(x: T | None): if x is None: assert_type(x, None) else: assert_type(x, T) assert_type(x, T | None) if x is not None: assert_type(x, T) else: assert_type(x, None)

NoneTypeNone等价

None的特殊之处在于运行时实例的名字也可以作为类型使用:对象None是类型None的实例。需要区分二者时,可使用自 Python 3.10 起可用的拼写NoneType,它与None等价:

[environment] python-version = "3.10"
from types import NoneType from ty_extensions import static_assert from ty_extensions._internal import is_equivalent_to static_assert(is_equivalent_to(NoneType, None))

整数Literal类型:值与对象身份的分离

整数字面量类型表示具有某个特定值的所有整数对象的集合,例如Literal[54165]。见 integer_literals.md。

整数Literal不是单例类型

一个整数类型不一定是单例类型:可能有多个运行时对象(位于不同内存地址)表示同一个整数值。如下代码可能打印False

x = 54165 y = 54165 print(x is y)

在实践中,CPython 3.13.0 下以脚本方式执行会打印True,但在 REPL 中会打印False。由于这是运行时实现细节,ty 将所有整数字面量类型建模为非单例类型

from ty_extensions import static_assert from ty_extensions._internal import is_singleton from typing import Literal static_assert(not is_singleton(Literal[0])) static_assert(not is_singleton(Literal[1])) static_assert(not is_singleton(Literal[54165]))

这直接影响类型收窄:不能通过is not判断变量不是某个整数字面量类型(虽然这本来也不是推荐做法):

def f(x: int): if x is 54165: # 若 x 与左侧字面量是同一对象,则值必然相同 reveal_type(x) # revealed: Literal[54165] if x is not 54165: # 无法收窄为 int & ~Literal[54165],因为 x 可能值相同但对象身份不同 reveal_type(x) # revealed: int

相等性收窄

整数Literal可以在相等比较中收窄类型:

def f(x: int): if x == 54165: # 默认情况下,ty 假定宽泛的 int 使用内建相等性; # 开启 strict-equality-semantics 会禁用此收窄 reveal_type(x) # revealed: Literal[54165] if x != 54165: reveal_type(x) # revealed: int & ~Literal[54165]

子类型关系

  • 整数Literal都是int的子类型;但int不等价于所有整数字面量的并集,因为True/Falseint的成员却不是任何整数字面量类型的成员:Literal[True]不是Literal[1]的子类型,Literal[False]也不是Literal[0]的子类型。
  • int可被继承,子类实例同样是int的子类型(CustomInt(int)int的子类型)。
  • 整数Literal不是float/complex的子类型,但 typing 规范对floatcomplex有特殊规定——接受整数(因此也接受整数字面量)的地方可以使用float/complex。ty 为此把注解float识别为int | float,并暴露JustFloatJustComplex两个内部类型用于测试:
from ty_extensions import static_assert, JustFloat, JustComplex from ty_extensions._internal import is_subtype_of from typing import Literal static_assert(not is_subtype_of(Literal[0], JustFloat) and not is_subtype_of(Literal[0], JustComplex)) static_assert(not is_subtype_of(Literal[54165], JustFloat) and not is_subtype_of(Literal[54165], JustComplex)) a: JustFloat = 1 # error: [invalid-assignment] b: JustComplex = 1 # error: [invalid-assignment] x: float = 1 y: complex = 1
  • 一个整数字面量类型能被命名的子类型只有它自身与Never

不相交性

两个整数字面量类型Literal[a]Literal[b]a != b时不相交。

整数字面量算术:把数学写进类型系统

ty 支持在整数字面量类型上进行一系列算术运算,甚至能静态验证 (3, 4, 5) 是勾股三元组:

from ty_extensions import static_assert static_assert(3**2 + 4**2 == 5**2)

借助整数字面量的并集,还可以在有限定义域上解方程(判断解是否存在):

from typing import Literal, assert_type type Nat = Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] def pythagorean_triples(a: Nat, b: Nat, c: Nat): # 答案是 bool,因为确实存在解(3² + 4² = 5²) assert_type(a**2 + b**2 == c**2, bool) def fermats_last_theorem(a: Nat, b: Nat, c: Nat): # 答案是 Literal[False],因为不存在解 assert_type(a**3 + b**3 == c**3, Literal[False])

真值性

0恒为假外,整数字面量恒为真。这可用于类型收窄:

def f(x: Literal[0, 1, 54365]): if x: assert_type(x, Literal[1, 54365]) else: assert_type(x, Literal[0])

tuple类型:乘积类型与丰富的结构语义

元组是 ty 类型系统中语义最丰富的部分之一,见 tuple.md。

元组作为乘积类型

tuple[P, Q]的成员是有序对(p, q)pP的成员,qQ的成员),类比集合的笛卡尔积:

class P: ... class Q: ... def _(p: P, q: Q): assert_type((p, q), tuple[P, Q])

实例化tuple

像所有类一样,调用tuple类可以实例化元组;实例化特化版本时会校验传入值是否匹配元素类型:

from typing_extensions import Iterable, Never reveal_type(tuple()) # revealed: tuple[()] reveal_type(tupleint)) # revealed: tuple[int] reveal_type(tuple[int, *tuple[str, ...]]((1,))) # revealed: tuple[int, *tuple[str, ...]] reveal_type(().__class__()) # revealed: tuple[()] reveal_type((1, 2).__class__((1, 2))) # revealed: tuple[Literal[1], Literal[2]]

行为细节示例(同一文档中验证):

  • tuple(x)(x 为Iterable[int])→tuple[int, ...]tuple(z)(z 为Never)→tuple[Unknown, ...]
  • tuple(aa)(aa 为list[Never])→tuple[()]——因为list[Never]的成员只能是空列表;
  • tuple((1, 2))tuple[Literal[1], Literal[2]]tuple([1])tuple[int, ...]
  • tupleinttupleint, str)分别报[invalid-argument-type]
  • 缺失必选参数iterable时(如(1,).__class__())报[missing-argument]

实例化元组子类

元组子类继承其元组父类特化版本的构造器,包括可变参数元组子类(如tuple[Any, ...])、空元组子类(tuple[()])、单元素元组子类(tuple[int])、同质元组子类(tuple[int, ...])以及混合元组子类(tuple[int, *tuple[str, ...]])。空元组子类传入非空实参、混合子类元素类型不匹配、缺失实参等都会触发对应错误([invalid-argument-type]/[missing-argument])。

元组实例的元类型

tuple[str, int]的成员不仅是tuple的精确实例,还包括tuple[str, int]的任何子类的实例。因此tuple[str, int]的元类型应是type[tuple[str, int]]而非<class 'tuple[str, int]'>——后者会错误地暗示所有实例的__class__都是运行时对象tuple

def f(x: tuple[int, ...], y: tuple[str, str], z: tuple[int, *tuple[str, ...], bytes]): reveal_type(type(x)) # revealed: type[tuple[int, ...]] reveal_type(type(y)) # revealed: type[tuple[str, str]] reveal_type(type(z)) # revealed: type[tuple[int, *tuple[str, ...], bytes]]

子类型关系

tuple[S1, S2]tuple[T1, T2]的子类型,当且仅当S1 <: T1S2 <: T2(依此类推其他长度);不同长度的元组之间不存在子类型关系:

static_assert(is_subtype_of(tuple[S1], tuple[T1])) static_assert(not is_subtype_of(tuple[T1], tuple[S1])) static_assert(is_subtype_of(tuple[S1, S2], tuple[T1, T2])) static_assert(not is_subtype_of(tuple[T1, S2], tuple[S1, T2])) static_assert(not is_subtype_of(tuple[S1], tuple[T1, T2]))

空元组tuple[()]

空元组的类型写作tuple[()],它不是单例类型(不同()实例不保证是同一对象,尽管 CPython 当前如此),并且可以被继承:

static_assert(not is_singleton(tuple[()])) class AnotherEmptyTuple(tuple[()]): ... static_assert(not is_equivalent_to(AnotherEmptyTuple, tuple[()])) static_assert(is_subtype_of(AnotherEmptyTuple, tuple[()])) static_assert(is_assignable_to(AnotherEmptyTuple, tuple[()]))

同理,非空元组也不是单例类型——即便其所有元素都是单例(如tuple[None]不是单例)。

Never的元组

元组注解可以描述用户自定义子类,因此含必选Never元素的元组保留其形状而不化简为Never

static_assert(not is_equivalent_to(tuple[Never], Never)) static_assert(not is_equivalent_to(tuple[int, Never], Never)) static_assert(not is_equivalent_to(tuple[Never, *tuple[int, ...]], Never))

元组表达式同样保留元素类型:

def tuple_from_never(value: Never) -> None: reveal_type((value,)) # revealed: tuple[Never] reveal_type((1, value)) # revealed: tuple[Literal[1], Never]

可变长度部分Never时,该部分恒为空,元组实际不再是变长的:

static_assert(is_equivalent_to(tuple[Never, ...], tuple[()])) static_assert(is_equivalent_to(tuple[int, *tuple[Never, ...]], tuple[int])) static_assert(is_equivalent_to(tuple[int, *tuple[Never, ...], int], tuple[int, int]))

同质非空元组:用混合元组表达最小长度

同质元组tuple[int, ...]表示零个或多个元素;需要表达"一个或多个""两个或多个"时使用混合元组,且必选元素也可以出现在后缀:

def takes_zero_or_more(t: tuple[int, ...]) -> None: ... def takes_one_or_more(t: tuple[int, *tuple[int, ...]]) -> None: ... def takes_two_or_more(t: tuple[int, int, *tuple[int, ...]]) -> None: ... takes_one_or_more(()) # error: [invalid-argument-type] takes_one_or_more((1,))

无论必选元素在前缀还是后缀,元组类型彼此等价(前后缀与变长部分的类型等价即可,不必完全相同):

static_assert(is_equivalent_to(tuple[int, *tuple[int, ...]], tuple[*tuple[int, ...], int])) static_assert(is_equivalent_to(tuple[int, int, *tuple[int, ...]], tuple[*tuple[int, ...], int, int]))

元组的不相交性

  • 最小长度不相容的两个元组(一个的最小长度大于另一个的最大长度)恒不相交;
  • 必含元素逐一比对:tuple[P1, P2]tuple[Q1, Q2]只要P1Q1P2Q2不相交即不相交(用@final类可验证);
  • 变长部分永远不会导致不相交(所有变长元组类型都包含空元组);
  • 元组类型与任意实例类型建模为不相交,因为tuple允许被继承;但异构元组之间建模为不相交,为此显式禁止两个不同特化的异构元组共存于同一 MRO(否则报[invalid-generic-class])。

元组的真值性

  • 空元组的真值恒为Falsebool(())的类型是Literal[False]tuple[()]可赋值给AlwaysFalsy
  • 非空元组真值恒为True——即使所有元素为假、甚至元素是渐进类型,因为元组真值只取决于长度:bool((False,))的类型是Literal[True]
  • 变长元组的真值有歧义(既包含空元组又包含非空元组),既不可赋值给AlwaysFalsy也不可赋值给AlwaysTruthy
  • 混合元组只要有一个必选元素就恒为真(tuple[int, *tuple[Any, ...]]可赋值给AlwaysTruthy)。这些结论与"元组可被继承、子类可覆写__bool__/__len__"存在已知冲突(文档示例NotAlwaysTruthyTuple),属当前实现的已知权衡。

未特化元组与Literal提升

未特化的tuple等价于tuple[Any, ...]tuple[Unknown, ...]

对于未注解的大元组,ty 只在元素数 ≤64 时推断Literal类型;超过 64 个元素则提升元组内部的Literal类型(has_64_elements保留全部Literalhas_65_elements整体拓宽为int)。Final注解、显式类型注解(如tuple[Literal[1], Literal[2], *tuple[int, ...], ...])可避免该提升。限制按"由字面量展开引入的元素"计数,空展开不消耗预算;64 元素展开保留字面量,多加一个元素则整体拓宽。

元组展开的其他细节

  • 元组中的星号展开保留类型:(1, *range(3), 3)tuple[Literal[1], *tuple[int, ...], Literal[3]]
  • 由列表字面量展开构造元组时保留每个元素的类型与位置:(*[1, "two"],)tuple[Literal[1], Literal["two"]],解包后first/second分别是Literal[1]/Literal["two"]
  • 嵌套展开(含海象运算符)仍保持各自容器的绑定;
  • 无效展开只报告一次迭代错误,周围字面量元素保留位置;
  • 展开含变长元组(含符号化TypeVarTuple)时保留两侧固定元素。

转换到其他Sequence类型

对协变容器(如frozenset),理想行为是不把Literal提升为其实例超类型(提升反而引入更多误报):frozenset((1, 2, 3))frozenset[Literal[1, 2, 3]]。但对不变容器(如list),Literal总是被提升:list((1, 2, 3))list[int]

Any:渐进类型系统的动态类型

Any是 Python 渐进类型系统中的动态类型,表示未知的静态类型,即一个未知的运行时值集合。见 any.md。

双向可赋值

每个类型都可赋值给AnyAny也可赋值给每个类型(含Neverobjecttype[Any]等):

static_assert(is_assignable_to(C, Any)) static_assert(is_assignable_to(Any, C)) static_assert(is_assignable_to(Never, Any)) static_assert(is_assignable_to(Any, Never)) static_assert(is_assignable_to(Any, Any))

Any | T:带下界的未知静态类型

Any与全静态类型T的并集描述"至少与T一样大"的未知值集合,即下界为T的未知全静态类型:

class Big: ... class Medium(Big): ... class Small(Medium): ... static_assert(is_assignable_to(Any | Medium, Big)) static_assert(is_assignable_to(Any | Medium, Medium)) static_assert(not is_assignable_to(Any | Medium, Small))

Any | object等价于object(与所有含object的并集一致)。

Any & T:带上界的未知静态类型

Any与全静态类型T的交集描述"不大于T"的未知值集合,即上界为T的未知全静态类型:

static_assert(is_assignable_to(Small, Any & Medium)) static_assert(is_assignable_to(Medium, Any & Medium)) static_assert(not is_assignable_to(Big, Any & Medium))

Any & Never描述"不大于Never的未知全静态类型"——除Never自身外不存在这样的类型,因此Any & Never等价于Never

Any的元组:对规范"渐进类型"的直接验证

文档引用了 typing 规范 type system concepts 中关于渐进类型的论述:tuple[int, Any]不表示单一集合,而是一个有界范围的可能的取值集合tuple[int, object]才是全静态类型。二者的差异可观察:tuple[int, Any]可赋值给tuple[int, int],而tuple[int, object]不行:

static_assert(is_assignable_to(tuple[int, Any], tuple[int, int])) static_assert(not is_assignable_to(tuple[int, object], tuple[int, int]))

AlwaysTruthyAlwaysFalsy:可静态推断的真值性

AlwaysTruthy/AlwaysFalsy描述"恒为真/恒为假"的值集合:at属于AlwaysTruthy当且仅当可静态推断bool(at)恒为True(类型为Literal[True]);af属于AlwaysFalsy当且仅当bool(af)恒为False。见 always_truthy_falsy.md。

典型成员

  • AlwaysTruthyTrue、任意非零整数(1123-1)、非空字符串/字节串、__bool__返回Literal[True]的自定义类型实例;
  • AlwaysFalsyFalseNone0、空字符串/字节串、__bool__返回Literal[False]的自定义类型实例。

不相交性与派生态

由定义直接可得AlwaysTruthyAlwaysFalsy不相交。进一步定义:

  • Truthy = ~AlwaysFalsy可能为真的值);
  • Falsy = ~AlwaysTruthy可能为假的值);
  • AmbiguousTruthiness = Truthy & Falsy(既可能真也可能假的值,其交集非空)。

随机返回True/False__bool__实现、可能返回空列表的maybe_empty_list()都属于歧义真值类别,bool()类型为bool。文档也标注了若干 TODO:目前把maybe_empty_list()赋给Truthy/Falsy/AmbiguousTruthiness会报[invalid-assignment],但按定义这些赋值应当成立。

子类型关系

只含恒真值的类型是AlwaysTruthy的子类型(Literal[True]Literal[1]Literal["non empty"]等);只含恒假值的类型是AlwaysFalsy的子类型(NoneLiteral[False]Literal[0]Literal[""]Literal[False] | Literal[0]等);真值有歧义的类型两者都不是(boollist[int]既不是AlwaysTruthy也不是AlwaysFalsy的子类型)。

开放问题与关联材料

tuple[()]是否恒为假目前仍在讨论中(对应 issue astral-sh/ruff#15528),当前实现将其建模为AlwaysFalsy的子类型。真值收窄相关的完整测试见 crates/ty_python_semantic/resources/mdtest/narrow/truthiness.md。

补类型~T:集合论中的"非"

~TT的补类型,描述所有不在T中的值。见 not_t.md。

核心性质

  • T不相交~TT不相交,与T的子类型S也不相交;
  • 并集为全量T | ~T等价于object
  • 反转子类型关系:若S <: T,则~T <: ~S(类比逻辑否定反转<=的方向);
  • 反转可赋值关系S可赋值给T蕴含~T可赋值给~SAny & S可赋值给Any & T蕴含~(Any & S)可赋值给~(Any & T)
  • 不相交蕴含子类型:若PQ不相交,则P <: ~QQ <: ~P(对@final类成立)。

德摩根定律

在集合论类型的语境下,德摩根定律成立:

static_assert(is_equivalent_to(~(P | Q), ~P & ~Q)) static_assert(is_equivalent_to(~(P & Q), ~P | ~Q))

渐进类型的否定

Any表示未知值集合,~Any同样表示未知值集合,二者等价:

static_assert(is_equivalent_to(~Any, Any))

这些事实表如何验证 ty 的实现

理解这些文档的验证机制,有助于把它们当作"可执行规范"使用:

  1. static_assert断言:来自ty_extensions(ty 项目自带的测试辅助包,见 python/ruff 之外的 ty 相关资源),在 mdtest 执行阶段由 ty 检查器对断言中的类型关系进行求值,is_subtype_ofis_assignable_tois_disjoint_fromis_equivalent_tois_singleton等即类型关系的判定接口;
  2. reveal_type/assert_type:直接呈现 ty 对表达式推断出的静态类型,注释# revealed: ...中的内容即期望值,也是测试断言;
  3. [environment]TOML 块:指定该文档(或该代码块)的python-version(如"3.14""3.12""3.11""3.10"),说明部分语义(如NoneType可用性、Literal提升行为)依赖 Python 版本;
  4. 错误注释# error: [invalid-assignment]# error: [invalid-argument-type]# error: [missing-argument]# error: [not-iterable]# error: [missing-type-argument]# error: [invalid-generic-class]等标注期望触发的诊断代码,部分还带完整消息文本。

这些文档正是"规范与实现一致性"的守门员:任何一处行为偏差都会导致 mdtest 失败,从而推动 ty 的实现(位于 crates/ty_python_semantic/src)始终与文档描述一致。

小结

type_compendium用一组可执行的事实表,把 Python 类型系统中最微妙的部分——底类型Never与顶类型object的对偶、None与整数字面量的单例性差异、元组的乘积/变长/子类化语义、Any的渐进性、基于__bool__的真值性分类以及集合论补类型~T的代数性质——精确地固定下来。对 ty 用户而言,它是理解收窄行为与边界语义的权威参考;对 ty 开发者而言,它是约束实现、防止回归的活测试集。阅读时建议结合 crates/ty_python_semantic/src 的实现与 crates/ruff_mdtest 的测试框架,将文档断言与底层判定逻辑相互印证。

【免费下载链接】ruffAn extremely fast Python linter and code formatter, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/ru/ruff

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询