Ruff ty 类型检查器中的typing.Self:方法签名、属性注解与接收者规则的完整语义指南
【免费下载链接】ruffAn extremely fast Python linter and code formatter, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/ru/ruff
typing.Self是 Python 3.11 引入的"自身类型"标注,用于表达"返回/接收当前类实例"的递归类型关系。Ruff 仓库内嵌的类型检查器 ty(位于 crates/ty_python_semantic)通过 mdtest 测试套件对其进行了极其详尽的语义验证——本文以 crates/ty_python_semantic/resources/mdtest/annotations/self.md 这份 1600 余行的测试文档为骨架,系统讲解 ty 中Self的完整行为:它如何被建模为绑定到当前类的TypeVar,如何作用于实例方法、类方法、属性、泛型类、协议与元类,以及哪些位置属于非法用法。
Self的本质:一个绑定到当前类的类型变量
ty 的语义模型将Self视为"被绑定到其所在类的TypeVar",这一点与TypeVar的约束求解机制完全一致。文档开篇即给出这一核心论断:
[environment] python-version = "3.13"from typing import Self class Shape: def set_scale(self: Self, scale: float) -> Self: reveal_type(self) # revealed: Self@set_scale return self注意reveal_type(self)的输出是Self@set_scale——这里的@set_scale后缀表示该Self类型变量与set_scale方法绑定,它是方法级的类型变量,而非类级。这一"按方法绑定"的设计是理解后续所有替换规则的关键。
在源码实现上,ty 用一个专门的TypeVarKind::TypingSelf来标记这类类型变量。在 crates/ty_python_semantic/src/types/typevar.rs 中可以看到判定逻辑:
pub(crate) fn is_self(self, db: &'db dyn Db) -> bool { matches!(self.kind(db), TypeVarKind::TypingSelf) }当方法被绑定(bound)时,ty 会把这个Self类型变量替换为具体的实例类型。该替换逻辑集中在 crates/ty_python_semantic/src/types/method.rs 的typing_self_type与map_self_type中:前者返回"应当替换所有typing.Self注解的类型"(通常是self/cls的绑定实例类型),后者把这一替换映射到方法签名上。
一个直观的验证是子类场景:Circle继承Shape的set_scale,当通过Circle()调用时,Self会绑定为Circle:
class Circle(Shape): def set_scale(self: Self, scale: float) -> Self: reveal_type(self) # revealed: Self@set_scale return self reveal_type(Shape().nested_type()) # revealed: list[Shape] reveal_type(Shape().nested_func()) # revealed: Shape未注解的self参数:隐式Self
文档强调:实例方法中第一个参数(无论其名字是什么)在未显式注解时,默认被推断为typing.Self;而@classmethod与@staticmethod不适用这一规则。self这个名字本身没有任何特殊含义。
from typing import Self class A: def __init__(self): reveal_type(self) # revealed: Self@__init__ def __init_subclass__(cls, default_name, **kwargs): reveal_type(cls) # revealed: type[Self@__init_subclass__] def implicit_self(self) -> Self: reveal_type(self) # revealed: Self@implicit_self return self def implicit_self_genericT -> T: reveal_type(self) # revealed: Self@implicit_self_generic return x注意__init__与__init_subclass__分别属于实例方法和类方法语义:__init_subclass__的cls被推断为type[Self@__init_subclass__]。
对外层作用域的验证如下:
a = A() reveal_type(a.implicit_self()) # revealed: A reveal_type(a.implicit_self) # revealed: bound method A.implicit_self() -> A嵌套函数中的self与外层方法的绑定。ty 会追踪嵌套作用域:内层函数中出现Self时,它绑定到最外层方法的Self,而不是内层函数自己:
class Shape: def nested_func(self: Self) -> Self: def inner() -> Self: reveal_type(self) # revealed: Self@nested_func return self return inner()显式调用验证第一个参数
当以未绑定方式显式调用实例方法时,ty 会校验第一个参数的类型:
A.implicit_self(a) # OK # error: [invalid-argument-type] "Argument to function `A.implicit_self` is incorrect: Argument type `Literal[1]` does not satisfy upper bound `A` of type variable `Self`" A.implicit_self(1)从这条错误信息可以看到一个关键实现事实:Self在内部被建模为"以上界(upper bound)约束的类型变量",其约束上界就是当前类。当传入Literal[1]不满足上界A时,约束求解失败并报错。
隐式传参(即obj.method()形式)同样校验:
from typing import Never, Callable class Strange: def can_not_be_called(self: Never) -> None: ... # error: [invalid-argument-type] "Argument to bound method `Strange.can_not_be_called` is incorrect: Expected `Never`, found `Strange`" Strange().can_not_be_called()类方法 / 静态方法不推断Self
A.a_classmethod() # OK A.a_classmethod(a) # error: [too-many-positional-arguments] A.a_staticmethod(1) # OK a.a_staticmethod(1) # OK A.a_staticmethod(a) # error: [invalid-argument-type]参数名、位置与装饰器都不影响推断
"第一个参数"的判定只看位置,不看名字;位置限定符、装饰器、property、async等均不影响:
def some_decorator**P, R -> Callable[P, R]: return f class B: def name_does_not_matter(this) -> Self: reveal_type(this) # revealed: Self@name_does_not_matter return this def positional_only(self, /, x: int) -> Self: ... def keyword_only(self, *, x: int) -> Self: ... @some_decorator def decorated_method(self) -> Self: ... @property def a_property(self) -> Self: ... async def async_method(self) -> Self: ... @staticmethod def static_method(self): # 参数可以叫 self,但不会被当作 Self reveal_type(self) # revealed: Unknown reveal_type(B().name_does_not_matter()) # revealed: B reveal_type(B().positional_only(1)) # revealed: B reveal_type(B().keyword_only(x=1)) # revealed: B reveal_type(B().decorated_method()) # revealed: B reveal_type(B().a_property) # revealed: B async def _(): reveal_type(await B().async_method()) # revealed: B反过来,自由函数与普通嵌套函数不使用隐式Self:
def not_a_method(self): reveal_type(self) # revealed: Unknown # error: [invalid-type-form] def does_not_return_self(self) -> Self: return self class C: def outer(self) -> None: def inner(self): reveal_type(self) # revealed: Unknown reveal_type(not_a_method) # revealed: def not_a_method(self) -> Unknown不同位置的Self是不同类型:绑定替换只作用于本方法
文档用一段专门的章节强调:方法 A 签名里的Self与方法 B 签名里的Self是彼此独立的类型变量。当访问绑定方法x.foo时,ty 只替换Foo.foo中出现的Self@foo,绝不会因为x本身的类型里恰好也含有一个Self(例如Foo[Self@bar])而把它牵连进来。
from typing import Self class Foo[T]: def foo(self: Self) -> T: raise NotImplementedError class Bar: def bar(self: Self, x: Foo[Self]): # revealed: bound method Foo[Self@bar].foo() -> Self@bar reveal_type(x.foo) reveal_type(x.foo()) # revealed: Self@bar def fU: Bar: # revealed: bound method Foo[U@f].foo() -> U@f reveal_type(x.foo) reveal_type(x.foo()) # revealed: U@f如果 ty 盲目替换所有Self,这里x.foo()就会错误地返回Foo[Self@bar]。正确的实现是:只替换Foo.foo自身的Self绑定,因此返回Self@bar(或泛型函数下的U@f)。这正是 method.rs 中map_self_type只对"本方法签名"做替换的原因。
类方法中的Self
显式接收者:cls: type[Self]
class Shape: def foo(self: Self) -> Self: return self @classmethod def bar(cls: type[Self]) -> Self: reveal_type(cls) # revealed: type[Self@bar] return cls() class Circle(Shape): ... reveal_type(Shape().foo()) # revealed: Shape reveal_type(Shape.bar()) # revealed: Shape reveal_type(Circle().foo()) # revealed: Circle reveal_type(Circle.bar()) # revealed: Circle隐式接收者
未注解的cls在类方法中同样被推断为type[Self],行为与显式版本完全一致:
class Shape: @classmethod def bar(cls) -> Self: reveal_type(cls) # revealed: type[Self@bar] return cls()泛型类中的隐式类方法
当类本身带类型参数时,Self的绑定会保留实例化的类型实参:
class GenericShape[T]: def foo(self) -> Self: ... @classmethod def bar(cls) -> Self: ... @classmethod def bazU -> "GenericShape[U]": reveal_type(cls) # revealed: type[Self@baz] # error: [invalid-return-type] return cls() class GenericCircleT: ... reveal_type(GenericShape().foo()) # revealed: GenericShape[Unknown] reveal_type(GenericShape.bar()) # revealed: GenericShape[Unknown] reveal_type(GenericShape[int].bar()) # revealed: GenericShape[int] reveal_type(GenericShape.baz(1)) # revealed: GenericShape[Literal[1]] reveal_type(GenericCircle[int].bar()) # revealed: GenericCircle[int]注意GenericShape().bar()得到GenericShape[Unknown]——未指定类型实参时,类型参数被推断为Unknown,但Self的绑定机制不变。
super()调用保留子类的Self
当子类覆盖父类返回Self的方法并调用super().method()时,返回类型必须是子类的Self类型变量,而不是具体子类类型(回归测试对应 ty 的 issue #2122)。这对普通方法与类方法均成立:
class Parent: def copy(self) -> Self: return self class Child(Parent): def copy(self) -> Self: result = super().copy() reveal_type(result) # revealed: Self@copy return result reveal_type(Child().copy()) # revealed: Child # 类方法版本 class Child2(Parent): @classmethod def create(cls) -> Self: result = super().create() reveal_type(result) # revealed: Self@create return result reveal_type(Child2.create()) # revealed: Child2更进一步,继承的类方法在通过self实例访问时也必须保留方法自身的Self类型,且真值收窄(truthiness narrowing)不破坏这一绑定:
from typing import Self, assert_type class Child(Parent): def method(self) -> None: assert_type(self.create(), Self) class MaybeEmpty: @classmethod def create(cls, other: Self) -> Self: return cls() def copy_if_empty(self, other: Self) -> Self: if not self: assert_type(self.create(other), Self) return self.create(other) return selfSelf在属性注解中的语义
递归数据结构
Self最常见的实战价值在于表达递归类型,例如链表与树:
class LinkedList: value: int next_node: Self def next(self: Self) -> Self: reveal_type(self.value) # revealed: int return self.next_node reveal_type(LinkedList().next()) # revealed: LinkedListdataclass字段同样支持Self:
from dataclasses import dataclass from typing import Self @dataclass class Node: parent: Self | None = None Node(Node())类体注解中的Self与方法签名中的Self是同一个逻辑类型变量,即使内部绑定上下文不同。因此方法返回类体里用Self注解的属性时,两者必须视为同类型:
class Chain: next: Self value: int def advance(self: Self) -> Self: return self.next def advance_twice(self: Self) -> Self: return self.advance().advance() class SubChain(Chain): extra: str reveal_type(SubChain().advance()) # revealed: SubChain reveal_type(SubChain().advance_twice()) # revealed: SubChainSelf注解的属性流经泛型容器也正常工作(list[Self]、Self | None、循环遍历等场景):
class TreeNode: children: list[Self] parent: Self | None def first_child(self) -> Self | None: if self.children: return self.children[0] return None def all_descendants(self) -> list[Self]: result: list[Self] = [] for child in self.children: result.append(child) result.extend(child.all_descendants()) return result def root(self) -> Self: node = self while node.parent is not None: node = node.parent return node类型别名保留Self。type Identity[T] = T这类别名包裹Self时,绑定不会被别名"截断":
type Identity[T] = T class AliasedNode: parent: Identity[Self] def __init__(self) -> None: self.parent = self reveal_type(AliasedNode().parent) # revealed: AliasedNode返回Self的可调用属性
属性被注解为Callable[[], Self]时,调用结果绑定到具体类:
from typing import Callable, Self class Factory: maker: Callable[[], Self] def __init__(self) -> None: self.maker = lambda: self class Sub(Factory): pass def _(s: Sub): reveal_type(s.maker()) # revealed: Sub泛型类与Self
保留类型实参
from typing import Self, Generic, TypeVar T = TypeVar("T") class Container(Generic[T]): value: T def set_value(self: Self, value: T) -> Self: return self int_container: Container[int] = Container[int]() reveal_type(int_container) # revealed: Container[int] reveal_type(int_container.set_value(1)) # revealed: Container[int]未绑定的继承方法
当继承的方法返回Self时,其返回类型是传入实例的类型——包括子类及其类型实参,即使调用时写的是Child而非Child[int]:
class Parent[T]: def get_self(self) -> Self: return self class ChildU: ... def _(child: Child[int]): reveal_type(Child.get_self(child)) # revealed: Child[int]带约束类型变量的泛型类
对带有界的类型参数(含NewType派生边界、联合边界)的实例调用方法不应产生错误(回归测试对应 ty 的 issue #2467):
from typing import NewType class Base: ... class C[T: Base]: x: T def g(self) -> None: pass C[Base]().g() # OK BaseNewType = NewType("BaseNewType", Base) C[BaseNewType]().g() # OK K = NewType("K", int) K2 = NewType("K2", K) class D[T: K]: def h(self) -> None: pass D[K]().h() # OK D[K2]().h() # OK泛型参数的默认值
带默认类型参数的类,Self方法会保留实例化时的类型实参;未实例化时使用默认值:
class Container[T = bytes]: def __init__(self: Self, data: T | None = None) -> None: self.data = data reveal_type(Container()) # revealed: Container[bytes] reveal_type(Container(1)) # revealed: Container[int] reveal_type(Container("a")) # revealed: Container[str] reveal_type(Container(b"a"))# revealed: Container[bytes] class Container2[T = bytes]: def method(self) -> Self: ... def _(c: Container2[str], d: Container2): reveal_type(c.method()) # revealed: Container2[str] reveal_type(d.method()) # revealed: Container2[bytes]旧的TypeVar(default=...)写法(回归测试对应 ty 的 issue #1156)行为一致:
T = TypeVar("T", default=bytes) class LegacyContainer(Generic[T]): def method(self) -> Self: ... def _(c: LegacyContainer[str], d: LegacyContainer): reveal_type(c.method()) # revealed: LegacyContainer[str] reveal_type(d.method()) # revealed: LegacyContainer[bytes]Self与 Protocol
协议中的Self遵循相同的绑定规则:Protocol自身被视为一个"类",方法/属性上的Self会绑定到具体的调用者类型:
from typing import Self, Protocol class Copyable(Protocol): def copy(self) -> Self: ... class Linkable(Protocol): next_node: Self def advance(self) -> Self: return self.next_node def _(l: Linkable) -> None: reveal_type(l.next_node) # revealed: Linkable class CopyableImpl: def copy(self) -> Self: ... class SubCopyable(CopyableImpl): ... def copy_it(x: Copyable) -> None: reveal_type(x.copy()) # revealed: Copyable def copy_concrete(x: CopyableImpl) -> None: reveal_type(x.copy()) # revealed: CopyableImpl def copy_sub(x: SubCopyable) -> None: reveal_type(x.copy()) # revealed: SubCopyable在注解位置(例如Self | None)使用时同样成立:
class Shape: def union(self: Self, other: Self | None): reveal_type(other) # revealed: Self@union | None return self非法用法与错误诊断
自由位置:函数签名与变量注解
Self不能用在自由函数、模块级变量、静态方法或类的基类列表中,统一报invalid-type-form:
from typing import Self, Generic, TypeVar T = TypeVar("T") # error: [invalid-type-form] def x(s: Self): ... # error: [invalid-type-form] b: Self class Foo: def return_concrete_type(self) -> Self: # error: [invalid-return-type] return Foo() @staticmethod # error: [invalid-type-form] "`Self` cannot be used in a static method" def make() -> Self: return Foo() class Bar(Generic[T]): ... # error: [invalid-type-form] class Baz(Bar[Self]): ...静态方法中的全面禁用
Self不能出现在静态方法的参数、返回类型、嵌套函数与默认参数值中:
class StaticMethodTests: @staticmethod # error: [invalid-type-form] "`Self` cannot be used in a static method" def with_self_return() -> Self: ... @staticmethod # error: [invalid-type-form] "`Self` cannot be used in a static method" def with_self_param(x: Self) -> None: ... @staticmethod def with_nested_function() -> None: # 静态方法内的嵌套函数中使用 Self 同样非法, # 因为 Self 绑定到最外层方法(即该静态方法) # error: [invalid-type-form] "`Self` cannot be used in a static method" def inner() -> Self: ... @staticmethod # error: [invalid-type-form] "`Self` cannot be used in a static method" def with_self_default(x: int = 0, y: "Self | None" = None) -> None: ...ty 对静态方法的识别相当健壮:别名后的staticmethod装饰器(sm = staticmethod)、完全限定的builtins.staticmethod、以及与泛型装饰器堆叠(无论顺序)都能被正确识别:
sm = staticmethod class AliasedStaticMethod: @sm # error: [invalid-type-form] "`Self` cannot be used in a static method" def aliased_static() -> Self: ... import builtins class BuiltinsStaticMethod: @builtins.staticmethod # error: [invalid-type-form] "`Self` cannot be used in a static method" def method() -> Self: ...__new__是唯一例外
__new__在运行时被解释器特殊处理为类似类方法,始终接收cls: type[Self]并返回Self,因此允许使用Self:
class WithNew: def __new__(cls) -> Self: instance = object.__new__(cls) return instance reveal_type(WithNew()) # revealed: WithNew class SubclassWithNew(WithNew): def __new__(cls) -> Self: return super().__new__(cls) reveal_type(SubclassWithNew()) # revealed: SubclassWithNew注意在 crates/ty_python_semantic/src/types/infer/builder/function.rs 的源码中,STATICMETHOD装饰器与__new__是并列判断的:静态方法返回None(不推断Self),而__new__与is_implicit_classmethod一起归入Self::Class分支。
元类中的禁用
ty 遵循 typing 规范(见文档末尾引用的规范链接),禁止在元类中使用Self,统一报invalid-type-form,错误消息为"Selfcannot be used in a metaclass"`:
class MyMetaclass(type): # error: [invalid-type-form] "`Self` cannot be used in a metaclass" registry: list[Self] # error: [invalid-type-form] "`Self` cannot be used in a metaclass" def __new__(cls, name, bases, dct) -> Self: ... # error: [invalid-type-form] "`Self` cannot be used in a metaclass" def instance_method(self) -> Self: ... @classmethod # error: [invalid-type-form] "`Self` cannot be used in a metaclass" def metaclass_classmethod(cls) -> Self: ... # 元类中的静态方法报的是 static method 错误 @staticmethod # error: [invalid-type-form] "`Self` cannot be used in a static method" def metaclass_staticmethod() -> Self: ...但注意边界情形:运行时使用名为self的参数值不报错(只有字面Self类型形式被禁止);间接继承type的类(如继承ABCMeta)也是元类;而使用元类的类(metaclass=...)本身不是元类,Self完全合法:
class AnnotableMeta(type): def __or__(self, other): return self # 无错误:这是运行时的 self,不是 Self 类型形式 class SomeMeta(type): ... class UsesMetaclass(metaclass=SomeMeta): def method(self) -> Self: reveal_type(self) # revealed: Self@method return self reveal_type(UsesMetaclass().method()) # revealed: UsesMetaclass嵌套类也遵循该规则:元类内的普通嵌套类不是元类(合法),但嵌套类若继承type则仍是元类(非法);enum.EnumMeta/enum.EnumType同样是元类,继承它的类中Self非法。
显式接收者注解:何时合法,何时冲突
文档后半部分系统整理了显式接收者注解的完整规则。ty 的实现位于 crates/ty_python_semantic/src/types/infer/builder/function.rs,accepts_annotation方法定义了严格的白名单:
- 实例方法接收者:只接受
Self(即Type::TypeVar且is_self为真); - 类方法接收者:只接受
type[Self](即SubclassOf内部是Self类型变量); - 其余任何注解都返回
false,触发invalid-type-form。
实例方法
class Valid: def implicit(self) -> Self: return self def explicit(self: Self) -> Self: return self class WithoutSelf: # 签名不使用 Self 时,允许其他接收者注解 def method(self: T) -> T: return self class Invalid: def type_variable(self: T) -> Self: # error: [invalid-type-form] ... def concrete(self: Invalid) -> Self: # error: [invalid-type-form] ... def union(self: T | None) -> Self: # error: [invalid-type-form] ... def class_object(self: type[Self]) -> Self: # error: [invalid-type-form] ...即使接收者非法,绑定方法的推断返回类型不受影响:
reveal_type(Invalid().concrete) # revealed: bound method Invalid.concrete() -> Invalid类方法
类方法的接收者可无注解或为type[Self];Self(不带type包裹)或type[T](T不是Self)均非法:
class Valid: @classmethod def implicit(cls) -> Self: return cls() @classmethod def explicit(cls: type[Self]) -> Self: return cls() class Invalid: @classmethod def instance(cls: Self) -> Self: # error: [invalid-type-form] ... @classmethod def type_variable(cls: type[T]) -> Self: # error: [invalid-type-form] ...收窄与别名不豁免非法性
联合即使化简为object也不豁免(返回与参数注解均如此);类型别名参数中的Self同样不豁免;每个Self出现位置各产生一条独立错误(Union[Self, Self]会产生两条指向各自位置的错误,并可用# ty: ignore[invalid-type-form]单独抑制其中一个):
class Example: def return_type(self: object) -> Self | object: ... # error: [invalid-type-form] def parameter(self: object, value: Self | object) -> None: ... # error: [invalid-type-form] type Identity[T] = T class Example2: def return_type(self: object) -> Identity[Self]: # error: [invalid-type-form] ... class SuppressedReturn: def method(self: object, other: Self) -> Self: # other: error: [invalid-type-form] ... # 返回注解可用 # ty: ignore[invalid-type-form] 抑制文档还给出了一条典型快照,展示诊断的精确定位能力:
error[invalid-type-form]: `Self` requires `self: Self` or `cls: type[Self]` for annotated receivers --> src/mdtest_snippet.py:12:43 | 12 | def method(self: object, other: Union[Self, Self]) -> None: ... | ^^^^泛型方法与引号包裹的Self
方法自身的类型参数不能替代接收者中的Self;同时引号包裹(from __future__ import annotations或字符串注解)不影响规则:
class Valid: def instanceT -> Self: return self @classmethod def class_methodT -> Self: return cls() class Invalid: def instanceT -> Self: # error: [invalid-type-form] ... class ValidQuoted: def instance(self: "Self") -> "Self": return self @classmethod def class_method(cls: "type[Self]") -> "Self": return cls()绑定方法固定Self
当方法被绑定(通过实例或类访问)时,签名中所有Self都被"固定"为已知的具体类型:
class C: def instance_method(self, other: Self) -> Self: return self @classmethod def class_method(cls) -> Self: return cls() # revealed: bound method C.instance_method(other: C) -> C reveal_type(C().instance_method) # revealed: bound method <class 'C'>.class_method() -> C reveal_type(C.class_method) class D(C): ... # revealed: bound method D.instance_method(other: D) -> D reveal_type(D().instance_method) # revealed: bound method <class 'D'>.class_method() -> D reveal_type(D.class_method)Self的绑定穿透类型别名、嵌套别名与"仅参数位置"(返回类型不含Self时参数中的Self仍会绑定):
type Identity[T] = T class Aliased: def copy(self, other: Identity[Self]) -> Identity[Self]: return other # revealed: bound method Aliased.copy(other: Aliased) -> Aliased reveal_type(Aliased().copy) class ParameterOnly: def consume(self, other: Identity[Self]) -> None: ... # revealed: bound method ParameterOnly.consume(other: ParameterOnly) -> None reveal_type(ParameterOnly().consume) ParameterOnly().consume(ParameterOnly()) # OK ParameterOnly().consume(object()) # error: [invalid-argument-type] class NestedChild(NestedAlias): ... # revealed: bound method NestedChild.copy(other: NestedChild) -> NestedChild reveal_type(NestedChild().copy)嵌套函数中的Self绑定到方法本身(即使Self注解先于方法中的绑定出现也如此),ty 内部提供ty_extensions._internal.generic_context与RegularCallableTypeOf来观察这一绑定过程:
from ty_extensions._internal import generic_context class C[T](): def f(self: Self): def b(x: Self): reveal_type(x) # revealed: Self@f reveal_type(generic_context(b)) # revealed: None # revealed: ty_extensions._internal.GenericContext[Self@f] reveal_type(generic_context(C.f))非位置首参数与存储的绑定方法
如果第一个参数不是位置参数(如*args, **kwargs),则不绑定self:
class C: def method(*args, **kwargs) -> None: ... # revealed: (...) -> None reveal_type(c) # c: RegularCallableTypeOf[C().method]其他对象存储为实例属性的绑定方法,其签名不受Self绑定影响(回归测试针对 jinjaLRUCache等项目的误报):
from collections import deque class MyClass: def __init__(self) -> None: self._queue: deque[int] = deque() self._append = self._queue.append def add(self, value: int) -> None: self._append(value)Django 风格模式:类属性中的泛型Self
Self作为泛型类的类型实参出现在类属性中时,类访问与实例访问都应绑定到具体类。这是 Django 风格Manager模式的典型场景:
from typing import Self, Generic, TypeVar T = TypeVar("T") class Manager(Generic[T]): def get(self) -> T: raise NotImplementedError class Model: objects: Manager[Self] class Confirmation(Model): expiry_date: int def test() -> None: # 类访问:Self 绑定到 Confirmation confirmation = Confirmation.objects.get() reveal_type(confirmation) # revealed: Confirmation x = confirmation.expiry_date # 可用——Confirmation 有 expiry_date # 实例访问:Self 同样绑定到 Confirmation instance = Confirmation() reveal_type(instance.objects) # revealed: Manager[Confirmation] instance_result = instance.objects.get() reveal_type(instance_result) # revealed: Confirmation同样的绑定在涉及描述符的属性中也成立:
class Descriptor(Generic[T]): def __get__(self, instance, owner) -> T: raise NotImplementedError class Base: attr: Descriptor[Self] = Descriptor() class Child(Base): ... reveal_type(Child.attr) # revealed: Child reveal_type(Child().attr) # revealed: Child如何运行这些测试
self.md属于 ty 的mdtest测试体系:以 Markdown 代码块的形式编排类型检查用例,代码中reveal_type(x) # revealed: ...声明期望的推断结果,# error: [code]声明期望的诊断,[environment]TOML 块配置运行环境(如python-version)。该框架的实现位于 crates/mdtest,其中:
- crates/mdtest/src/assertion.rs 负责解析断言语法(
revealed:、error:、snapshot等); - crates/mdtest/src/matcher.rs 的
match_reveal_type_diagnostic负责把类型检查器产出的reveal_type诊断与期望值逐一比对; - 支持
MDTEST_TEST_FILTER环境变量按名称过滤用例。
ty 类型检查器本体位于 crates/ty 与 crates/ty_python_semantic,Self相关的核心判定(TypeVarKind::TypingSelf、接收者白名单、绑定替换)分别落在 crates/ty_python_semantic/src/types/typevar.rs、crates/ty_python_semantic/src/types/infer/builder/function.rs 与 crates/ty_python_semantic/src/types/method.rs 中,读者可按图索骥深入研读实现细节。
小结
typing.Self看似简单,实则包含一整套精密的类型语义:它内部是一个绑定到当前类的类型变量(TypeVarKind::TypingSelf),按"方法"而非"类"划分绑定作用域;实例方法第一个未注解的位置参数隐式获得Self类型;绑定方法时Self被固定并替换为具体接收者类型(包括泛型实参);而静态方法、自由函数与元类中则全面禁止使用。把握住"Self是接收者类型的替身 + 绑定发生在方法层面"这两条主线,无论是编写递归数据结构、泛型类工厂方法,还是排查invalid-type-form诊断,都能准确预判 ty 的行为。
【免费下载链接】ruffAn extremely fast Python linter and code formatter, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/ru/ruff
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考