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

Python装饰器类型注解优化:如何让装饰后函数显示正确签名与文档?

解决Python装饰器的类型注解与签名显示问题

核心解决方案

要同时实现正确的签名显示、文档字符串继承以及新增参数的类型校验,需要结合functools.wraps和typing模块的ParamSpec/TypeVar来精准定义装饰器的类型,同时完善参数校验逻辑。

具体实现步骤

1. 导入必要模块

from functools import wraps
from typing import ParamSpec, TypeVar, Callable, Optional

# 假设你已定义以下业务类型
class DashboardAPI:
    """Meraki Dashboard API 实例类型"""
    pass

class MerakiKey:
    """Meraki API 密钥类型"""
    pass

2. 定义参数与返回类型变量

用ParamSpec保留原函数的参数结构,TypeVar保留返回类型,确保装饰器能适配任意符合结构的函数:

P = ParamSpec("P")  # 原函数的参数集合
RT = TypeVar("RT")  # 原函数的返回类型

3. 实现带类型注解的装饰器

def meraki_dashboard_setup(func: Callable[P, RT]) -> Callable[[Optional[DashboardAPI], Optional[MerakiKey], *P.args, **P.kwargs], RT]:
    @wraps(func)  # 继承原函数的文档字符串、名称等元数据
    def wrapper(
        dashboard: Optional[DashboardAPI] = None,
        meraki_key: Optional[MerakiKey] = None,
        *args: P.args,
        **kwargs: P.kwargs
    ) -> RT:
        # 运行时强制参数互斥校验
        if dashboard is not None and meraki_key is not None:
            raise ValueError("不能同时提供dashboard和meraki_key参数")
        if dashboard is None and meraki_key is None:
            raise ValueError("必须提供dashboard或meraki_key中的一个")
        
        # 根据meraki_key创建DashboardAPI实例
        if meraki_key is not None:
            dashboard = DashboardAPI()  # 替换为你的实际初始化逻辑
        
        # 调用原函数并传递处理后的dashboard
        return func(dashboard, *args, **kwargs)
    
    return wrapper

4. 使用装饰器

@meraki_dashboard_setup
def get_organization_networks(dashboard: DashboardAPI, org_id: str) -> list:
    """获取指定组织下的所有网络列表"""
    # 你的业务逻辑实现
    return []

效果说明

  • 签名显示:IDE会正确识别装饰后的函数签名为get_organization_networks(dashboard: Optional[DashboardAPI] = None, meraki_key: Optional[MerakiKey] = None, org_id: str) -> list,而非模糊的_Wrapped类型。
  • 文档继承:通过@wraps(func),原函数的文档字符串会被完整继承,调用时能正常查看。
  • 类型校验:IDE会对dashboard、meraki_key以及原函数的参数进行类型校验,运行时也会强制参数互斥规则。

版本兼容提示

如果使用Python 3.9及以下版本,需要从typing_extensions导入ParamSpec和Concatenate,并调整装饰器的返回类型注解为:

from typing_extensions import Concatenate

def meraki_dashboard_setup(func: Callable[P, RT]) -> Callable[Concatenate[Optional[DashboardAPI], Optional[MerakiKey], P], RT]:
    # 内部逻辑不变
    ...

内容的提问来源于stack exchange,提问作者Eddysanoli

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 15:53:17