为何使用装饰器包装函数后mypy无法正确检测其参数调用错误?
为何使用装饰器包装函数后mypy无法正确检测其参数调用错误?
你遇到的这个问题其实很典型,核心原因在于你的装饰器缺少正确的静态类型注解——虽然functools.wraps帮你把原函数的元数据(包括注解)复制到了包装后的函数上,但mypy作为静态类型检查工具,不会依赖运行时的函数属性来推导类型,它只会根据代码里的显式类型注解做分析。
咱们来拆解一下你的代码问题:
- 你给装饰器
myWrapper和内部的f都加了# type: ignore,这相当于直接让mypy跳过了对这段代码的类型检查。此时mypy无法得知这个装饰器会完全保留原函数的签名,只能默认认为被装饰后的hello函数是一个可以接受任意*args和**kwargs的函数,自然不会检测到参数过多的错误。 - 而未被装饰的
helloNoWrap有明确的参数和返回类型注解,mypy可以直接根据这些注解判断出调用时传入了过多参数,所以抛出了错误。
要解决这个问题,你需要给装饰器添加精确的泛型类型注解,告诉mypy这个装饰器会保留原函数的完整签名。比如可以用Python 3.9+引入的ParamSpec和TypeVar来实现:
import functools from typing import TypeVar, ParamSpec, Callable # 定义参数规格和返回值类型变量 P = ParamSpec('P') R = TypeVar('R') def myWrapper(func: Callable[P, R]) -> Callable[P, R]: @functools.wraps(func) def f(*args: P.args, **kwargs: P.kwargs) -> R: return func(*args, **kwargs) return f @myWrapper def hello(text: str, n: int = 1) -> str: return "hello world" def helloNoWrap(text: str, n: int = 1) -> str: return "hello world" # 此时mypy会检测到这两个调用都有参数过多的错误 hello("hello", 5, 4) helloNoWrap("hello", 5, 4)
这个注解的作用是:
P代表原函数的参数规格(包括参数类型、数量、默认值等)R代表原函数的返回值类型- 装饰器接受一个签名为
Callable[P, R]的函数,返回一个完全相同签名的函数
这样mypy就能准确识别被装饰后函数的类型,像检测未装饰函数一样,发现调用时的参数错误了。
补充一句:functools.wraps是运行时工具,它的作用是让包装后的函数在运行时看起来和原函数一致(比如保留函数名、注解等),但这对静态类型检查的mypy来说没用——mypy只认代码里写的静态类型注解。
备注:内容来源于stack exchange,提问作者David Pitchford
相关产品推荐
相关产品推荐

