ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

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

深入 ty 类型系统:Ruff 类型检查器 Type Compendium(类型事实表)全景解读 深入 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 类型系统中重要、有趣且特殊的类型——Never、object、None、整数Literal、tuple、Any、AlwaysTruthy/AlwaysFalsy以及补类型~T——的建模规则与判定逻辑。阅读本文后你将掌握 ty 在子类型、可赋值性、不相交性、并集/交集归约、类型收窄、真值性等核心关系上的精确语义并能理解这些事实表文档本身如何作为可运行的 mdtest 测试来约束实现与规范保持一致。Type Compendium 是什么一份活的类型系统参考手册type_compendium位于 crates/ty_python_semantic/resources/mdtest/type_compendium/README.md其中包含一系列关于重要、有趣且特殊的类型的事实表fact sheets。它承担两个职责教育性参考面向 ty 的开发者与用户以易于理解的方式说明 ty 对 Python 类型系统各类型的解释活文档living document这些文档同时是 mdtest 测试——文档中的每一段 Python 代码都会被真实执行与校验确保 ty 对类型及其属性的实现与规范保持一致。一旦实现行为与文档断言不符测试即失败。从仓库结构看mdtest 机制由 crates/mdtest 与 crates/ruff_mdtest 提供支持因此这些文档不只是读的更是跑的。事实表目录对应仓库文件NeverobjectNone整数Literaltuple类型AnyAlwaysTruthy、AlwaysFalsy~T文档中大量使用两个关键验证手段static_assert来自ty_extensions在编译期断言某类型关系成立以及reveal_type/assert_type揭示或断言某个表达式在 ty 眼中的静态类型。这些断言就是测试用例本身。Never空集与底类型Never是每个类型的子类型Never表示值的空集是 Python 类型系统中的底类型bottom type它是每个类型的子类型但除Never自身以及上界为Never的类型变量外没有任何类型是Never的子类型。见 never.mdfrom 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, boundNever) 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))Never与object的互补性object描述所有可能值的集合Never描述空集二者互为补集~object等价于Never~Never等价于object。这种对偶性贯穿始终关系Neverobject子类型是每个类型的子类型是每个类型的超类型可赋值性可赋值给每个类型可由每个类型赋值不相交性与每个类型不相交与每个类型重叠并集构建并集是 no-op与object求并集得object交集求交集得Never求交集是 no-oplist[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, ...] ()NoReturn与Never相同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不相交int、str、type、Any都与object重叠只有Never与object不相交。与object求并集结果恒为object即int | object | None等价于object与object求交集结果等价于原类型即P object Q等价于P Q。None单例类型None或NoneType见下文是只有一个成员——对象None——的单例类型。见 none.md。单例类型的子类型与其他单例类型一样None仅有的子类型是None自身与Neverstatic_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)NoneType与None等价None的特殊之处在于运行时实例的名字也可以作为类型使用对象None是类型None的实例。需要区分二者时可使用自 Python 3.10 起可用的拼写NoneType它与None等价[environment] python-version 3.10from 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不是单例类型一个整数类型不一定是单例类型可能有多个运行时对象位于不同内存地址表示同一个整数值。如下代码可能打印Falsex 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/False是int的成员却不是任何整数字面量类型的成员Literal[True]不是Literal[1]的子类型Literal[False]也不是Literal[0]的子类型。int可被继承子类实例同样是int的子类型CustomInt(int)是int的子类型。整数Literal不是float/complex的子类型但 typing 规范对float与complex有特殊规定——接受整数因此也接受整数字面量的地方可以使用float/complex。ty 为此把注解float识别为int | float并暴露JustFloat、JustComplex两个内部类型用于测试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)p是P的成员q是Q的成员类比集合的笛卡尔积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, ...]tupleint、tupleint, 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__都是运行时对象tupledef 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 : T1且S2 : 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元素的元组保留其形状而不化简为Neverstatic_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]只要P1与Q1或P2与Q2不相交即不相交用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]]。但对不变容器如listLiteral总是被提升list((1, 2, 3))→list[int]。Any渐进类型系统的动态类型Any是 Python 渐进类型系统中的动态类型表示未知的静态类型即一个未知的运行时值集合。见 any.md。双向可赋值每个类型都可赋值给AnyAny也可赋值给每个类型含Never、object、type[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]))AlwaysTruthy与AlwaysFalsy可静态推断的真值性AlwaysTruthy/AlwaysFalsy描述恒为真/恒为假的值集合at属于AlwaysTruthy当且仅当可静态推断bool(at)恒为True类型为Literal[True]af属于AlwaysFalsy当且仅当bool(af)恒为False。见 always_truthy_falsy.md。典型成员AlwaysTruthyTrue、任意非零整数1、123、-1、非空字符串/字节串、__bool__返回Literal[True]的自定义类型实例AlwaysFalsyFalse、None、0、空字符串/字节串、__bool__返回Literal[False]的自定义类型实例。不相交性与派生态由定义直接可得AlwaysTruthy与AlwaysFalsy不相交。进一步定义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的子类型None、Literal[False]、Literal[0]、Literal[]、Literal[False] | Literal[0]等真值有歧义的类型两者都不是bool、list[int]既不是AlwaysTruthy也不是AlwaysFalsy的子类型。开放问题与关联材料tuple[()]是否恒为假目前仍在讨论中对应 issue astral-sh/ruff#15528当前实现将其建模为AlwaysFalsy的子类型。真值收窄相关的完整测试见 crates/ty_python_semantic/resources/mdtest/narrow/truthiness.md。补类型~T集合论中的非~T是T的补类型描述所有不在T中的值。见 not_t.md。核心性质与T不相交~T与T不相交与T的子类型S也不相交并集为全量T | ~T等价于object反转子类型关系若S : T则~T : ~S类比逻辑否定反转的方向反转可赋值关系S可赋值给T蕴含~T可赋值给~SAny S可赋值给Any T蕴含~(Any S)可赋值给~(Any T)不相交蕴含子类型若P与Q不相交则P : ~Q且Q : ~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 的实现理解这些文档的验证机制有助于把它们当作可执行规范使用static_assert断言来自ty_extensionsty 项目自带的测试辅助包见 python/ruff 之外的 ty 相关资源在 mdtest 执行阶段由 ty 检查器对断言中的类型关系进行求值is_subtype_of、is_assignable_to、is_disjoint_from、is_equivalent_to、is_singleton等即类型关系的判定接口reveal_type/assert_type直接呈现 ty 对表达式推断出的静态类型注释# revealed: ...中的内容即期望值也是测试断言[environment]TOML 块指定该文档或该代码块的python-version如3.14、3.12、3.11、3.10说明部分语义如NoneType可用性、Literal提升行为依赖 Python 版本错误注释# 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),仅供参考
返回列表