Python带参异步装饰器类型标注修复 解决mypy子类签名不兼容报错
问题根因
mypy报签名不兼容的核心原因来自装饰器的类型标注和返回逻辑完全错位:
- 原代码中
dec2函数的返回值标注为Awaitable[T],但实际返回的是通过async def定义的可调用函数(本身类型为Callable[..., Awaitable[T]]),还通过cast强行将这个可调用函数伪装成协程对象。这种写法会让类型检查器误以为子类中被装饰的fn属性不是需要接收self参数的类方法,而是一个已经实例化完成的协程对象,和父类抽象方法要求的“可调用方法签名”完全不匹配。 - 强制类型转换进一步误导了类型推断:
wrapper函数本身是可调用对象,只有被调用后才会返回Awaitable协程,原代码直接将其cast为Awaitable类型,完全颠倒了函数和其调用返回值的类型关系。
调整方案
仅需要修正装饰器的类型标注逻辑、删除错误的强制类型转换,就能在保留装饰器全部运行逻辑的前提下,让mypy正确识别被装饰方法的原始签名,消除报错。调整后的完整代码如下:
from abc import ABC, abstractmethod from typing import Any, Awaitable, Callable, TypeVar T = TypeVar('T') def dec(args: Any) -> Callable[[Callable[..., Awaitable[T]]], Callable[..., Awaitable[T]]]: def dec2(f: Callable[..., Awaitable[T]]) -> Callable[..., Awaitable[T]]: async def wrapper(*wrapper_args: Any, **wrapper_kwargs: Any) -> T: print(wrapper_args) return await f(*wrapper_args, **wrapper_kwargs) # 直接返回wrapper可调用函数,无需错误的强制类型转换 return wrapper return dec2 class A(ABC): @abstractmethod async def fn(self) -> bool: pass class B(A): @dec('hi') async def fn(self) -> bool: return True class C(A): @dec('hi') async def fn(self) -> bool: return False
调整后的效果
- 装饰器的全链路返回类型标注和异步类方法的真实结构完全匹配,mypy可以正确推断出被装饰后的
fn依然是接收self参数、返回布尔值协程的类方法,和父类抽象方法签名完全兼容 - 移除了误导类型推断的强制类型转换,没有改变装饰器的实际运行逻辑:依然会在调用被装饰方法时打印传入的参数,正常等待原方法执行完成并返回结果
- 该写法完全兼容Python 3.7环境和0.790版本的mypy,不会触发原有的签名不兼容报错
内容的提问来源于stack exchange,提问作者Tyler Bream
相关产品推荐
相关产品推荐

