如何为Python带条件输出类型的函数装饰器添加类型注解?
为依赖输入参数的
lazy装饰器添加类型注解(不依赖mypy扩展) 问题背景
我有一组函数,均接受名为value的命名参数以及任意其他命名参数。实现了一个lazy装饰器:
- 当
value不为None时,被装饰函数正常返回结果; - 当
value为None时,返回一个partial函数。
现在需要为这个输出类型依赖于value输入的装饰器添加类型注解,且不依赖mypy扩展(原实现使用了mypy_extensions,希望替换掉)。
原实现代码:
from functools import partial def lazy(func): def wrapper(value=None, **kwargs): if value is not None: return func(value=value, **kwargs) else: return partial(func, **kwargs) return wrapper @lazy def test_multiply(*, value: float, multiplier: float) -> float: return value * multiplier @lazy def test_format(*, value: float, fmt: str) -> str: return fmt % value
解决方案
要实现不依赖mypy扩展的类型注解,核心是利用typing.Overload区分两种调用场景的返回类型,结合ParamSpec和TypeVar保留原函数的参数与返回类型信息。
完整注解后的代码:
from functools import partial from typing import Callable, TypeVar, ParamSpec, Overload, Optional # 定义类型变量和参数规范 R = TypeVar("R") P = ParamSpec("P") # 重载装饰器的返回类型:覆盖两种调用情况 @Overload def lazy(func: Callable[P, R]) -> Callable[[*, value: Optional[P.kwargs['value']], **P.kwargs], R | partial[Callable[[*, value: P.kwargs['value']], R]]]: ... def lazy(func: Callable[P, R]) -> Callable: def wrapper(value: Optional[Any] = None, **kwargs: P.kwargs) -> R | partial[Callable]: if value is not None: return func(value=value, **kwargs) else: return partial(func, **kwargs) return wrapper # 测试函数 @lazy def test_multiply(*, value: float, multiplier: float) -> float: return value * multiplier @lazy def test_format(*, value: float, fmt: str) -> str: return fmt % value
注解关键点说明
- 用
Overload区分调用场景:
定义重载签名,明确两种输入对应的返回类型:- 当
value不为None时,直接返回原函数的结果类型R; - 当
value为None时,返回绑定了其余参数的partial函数,这个partial仅需传入value即可执行。
- 当
ParamSpec+TypeVar保留原函数信息:P捕获原函数的所有参数类型(包括value和其他命名参数),R捕获原函数的返回类型,确保装饰器返回结果的类型与原函数完全匹配。- 抛弃mypy扩展依赖:
不再使用mypy_extensions中的特殊类型,直接通过ParamSpec引用原函数的参数类型,配合Overload覆盖所有调用场景,符合标准类型注解规范。
类型检查验证
使用mypy检查上述代码时,会正确识别以下场景:
- 调用
test_multiply(value=2.0, multiplier=3.0)时,返回类型为float; - 调用
test_multiply(value=None, multiplier=3.0)时,返回类型为partial[Callable[[*, value: float], float]]; - 对返回的
partial函数调用(value=2.0)时,会正确推断返回float类型。
内容的提问来源于stack exchange,提问作者Jetpac
相关产品推荐
相关产品推荐

