如何为扩展类成员的类装饰器添加类型注解?
解决类装饰器新增方法的类型识别问题
当然可以改进类型注解,让类型检查器正确识别到装饰器新增的new异步类方法!问题核心在于当前装饰器返回的是原类的Type[T],但T绑定的Optimizerable协议只定义了ainit方法,并没有包含我们动态添加的new方法。我们可以通过扩展协议并调整装饰器的返回类型来解决这个问题。
下面是修改后的完整代码,我会逐步解释关键改动:
from typing import ParamSpec, Protocol, TypeVar, Type, Callable AinitArguments = ParamSpec("AinitArguments") T = TypeVar("T", bound="Optimizerable") # 1. 定义基础协议,要求类必须包含ainit方法 class Optimizerable(Protocol): ainit: Callable[AinitArguments, None] # 2. 定义扩展协议,明确包含我们要添加的new异步类方法 class OptimizerWithNew(Optimizerable, Protocol[AinitArguments, T]): @classmethod async def new(cls: Type[T], *args: AinitArguments.args, **kwargs: AinitArguments.kwargs) -> T: ... # 3. 修改装饰器的返回类型为这个扩展协议的Type def optimizer(cls: Type[T]) -> Type[OptimizerWithNew[AinitArguments, T]]: @classmethod async def new(cls: Type[T], *args: AinitArguments.args, **kwargs: AinitArguments.kwargs) -> T: self = cls.__new__(cls) await self.ainit(*args, **kwargs) return self setattr(cls, "new", new) # 运行时我们确实给类添加了符合协议的方法,这里忽略类型检查器的不匹配提示 return cls # type: ignore[return-value] @optimizer class Myclass: async def ainit(self, a: int, b: float) -> None: pass # stub
关键改动说明:
- 扩展协议
OptimizerWithNew:这个协议继承自Optimizerable,同时明确声明了new异步类方法的签名——参数复用ainit的AinitArguments来保持一致,返回值为类实例T,让类型检查器知道被装饰后的类拥有这个方法。 - 装饰器返回类型调整:把原来的
Type[T]改成Type[OptimizerWithNew[AinitArguments, T]],直接告诉类型检查器,经过装饰的类现在符合这个扩展协议的要求。 - 类型忽略注释:因为原类在静态检查阶段还没有
new方法,类型检查器会认为返回值不匹配,加上# type: ignore[return-value]是安全的——我们确实在运行时动态添加了符合协议的方法。
现在你使用Myclass.new(1, 2.0)时,类型检查器会正确识别这个方法的参数要求和返回值类型,再也不会出现“找不到new方法”的类型提示啦!
内容的提问来源于stack exchange,提问作者Aart Stuurman
相关产品推荐
相关产品推荐

