如何实现兼容mypy --strict的函数包装且保留类型(无需重定义)?
问题场景
假设定义了这样一个带类型注解的函数:
def foo(arg: int, arg2: str, arg3: int) -> MyReturnType: # 函数实现
用functools.partial固定参数时,mypy能正确识别生成的safefoo的类型:
safefoo = functools.partial(foo, arg2="whatever")
但自己写包装函数(不管用不用@wraps),mypy都会报错Call to untyped function "safefoo" in typed context,比如这两种写法:
- 直接写包装函数:
def safefoo(*args, **kwargs): # 这里要加额外逻辑 return foo(*args, arg2="whatever", **kwargs)
- 用
functools.wraps装饰:
from functools import wraps @wraps(foo) def safefoo(*args, **kwargs): return foo(*args, arg2="whatever", **kwargs)
现在需要在不重新手动定义类型注解的前提下,让mypy认可这类自定义包装函数的类型。
解决办法
1. 用ParamSpec和TypeVar继承原函数类型(Python 3.10+)
利用标准库的类型工具,精准复用原函数的参数和返回类型定义,无需手动重复写:
from functools import wraps from typing import TypeVar, ParamSpec, Callable # 定义参数规范和返回值类型变量 P = ParamSpec("P") R = TypeVar("R") def wrap_fixed_arg(func: Callable[P, R], fixed_arg2: str) -> Callable[P, R]: @wraps(func) def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: # 在这里添加你的额外逻辑 return func(*args, arg2=fixed_arg2, **kwargs) return wrapper # 生成包装后的函数 safefoo = wrap_fixed_arg(foo, "whatever")
这种写法能让mypy完全推导safefoo的类型,和原函数foo的差异仅在于已固定的arg2参数。
2. 用typing.cast做类型转换
如果只是要快速通过mypy检查,可直接把包装函数强制转换为原函数对应的Callable类型:
from functools import wraps from typing import Callable, cast @wraps(foo) def safefoo(*args, **kwargs): # 你的额外逻辑 return foo(*args, arg2="whatever", **kwargs) # 把safefoo转换为符合要求的类型:接受int和int参数,返回MyReturnType safefoo = cast(Callable[[int, int], MyReturnType], safefoo)
注意:这种方式需要你清楚原函数的类型结构,若原函数类型后续变更,这里的转换语句也得同步调整。
3. 结合partial与包装逻辑
如果额外逻辑可以和参数固定操作拆分,也可以先通过partial固定参数,再在外层包装添加逻辑:
from functools import partial def safefoo(*args, **kwargs): # 先执行你的额外逻辑 # 用partial固定arg2参数 prefixed_foo = partial(foo, arg2="whatever") return prefixed_foo(*args, **kwargs)
这种写法借助partial自带的类型推导能力,让mypy间接认可safefoo的类型。
内容的提问来源于stack exchange,提问作者Tommy
相关产品推荐
相关产品推荐

