如何编写接受父类型任意子类型实例的Python类型提示?
如何用类型提示接受某类型的任意子类型实例
要实现接受SuperType任意子类型实例的类型提示,核心是利用协变TypeVar兼容子类实例与父类类型的赋值关系,同时通过类型约束区分实例方法和静态方法。
问题根源
你遇到的MyPy报错,是因为默认的TypeVar是不变的:Callable[[Child, int], None](子类实例方法的签名)无法被视为Callable[[Parent, int], None](父类实例方法的签名)的子类型。而我们需要的是允许子类实例替代父类实例的协变关系。
解决方案
1. 定义协变的TypeVar
将SuperType声明为协变,这样子类实例可以被安全地赋值给期望父类实例的参数:
from typing import * CallbackArgs = ParamSpec('CallbackArgs') CallbackReturn = TypeVar('CallbackReturn') # 声明协变TypeVar,允许子类实例替代父类实例 SuperType = TypeVar('SuperType', covariant=True)
2. 调整装饰器的类型签名
修改check函数的返回类型,让inner接受的函数第一个参数是SuperType(而非Type[SuperType]),这样就能匹配任意子类型的实例:
def check(func: Callable[Concatenate[SuperType, CallbackArgs], CallbackReturn]) \ -> Callable[[Callable[Concatenate[SuperType, CallbackArgs], CallbackReturn]], None]: def inner(function: Callable[Concatenate[SuperType, CallbackArgs], CallbackReturn]) -> None: return None return inner
3. 验证代码
现在你的示例代码可以通过MyPy检查,同时能正确约束类型:
class Parent: def func(self, a: int) -> None: ... class Child(Parent): # 类型检查通过:Child实例是Parent的子类型,符合协变要求 @check(Parent.func) def func(self, a: int) -> None: ...
4. 检测静态方法的错误使用
如果尝试将check用于静态方法,MyPy会直接报错,因为静态方法的第一个参数不是实例类型,无法匹配SuperType的约束:
class Child(Parent): @staticmethod @check(Parent.func) # MyPy报错:静态方法的签名不匹配期望的实例方法签名 def func(a: int) -> None: ...
关键说明
- 协变
TypeVar的作用:当SuperType被标记为协变时,Callable[[Child, ...], ...]会被视为Callable[[Parent, ...], ...]的子类型,符合面向对象中"里氏替换原则"的类型要求。 - 无需使用
Type[SuperType]:Type[SuperType]表示类本身(比如Parent或Child类对象),而我们需要的是类的实例,所以直接用SuperType即可。
内容的提问来源于stack exchange,提问作者mutableVoid
相关产品推荐
相关产品推荐

