You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 15:47:34