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

如何为添加命名参数的Python装饰器添加类型注解以识别参数名

问题

我有一个装饰器wrap_with_id,应用在函数func上:

from typing import ParamSpec, TypeVar, Callable

P = ParamSpec("P")
R = TypeVar("R")
all_ids = []

def wrap_with_id(fn: Callable[P, R]):
    def wrapper(id: str, *args: P.args, **kwargs: P.kwargs) -> R:
        all_ids.append(id)
        return fn(*args, **kwargs)

    return wrapper

@wrap_with_id
def func(x: int, y: int) -> str:
    return f"{x}-{y}"

用Pylance或Pyright时,能正确识别func的签名是func(id: str, x: int, y: int) -> str,但此时wrap_with_id没有返回类型注解。我尝试添加Callable[Concatenate[str, P], R]作为返回类型:

from typing import ParamSpec, TypeVar, Callable, Concatenate

P = ParamSpec("P")
R = TypeVar("R")
all_ids = []

def wrap_with_id(fn: Callable[P, R]) -> Callable[Concatenate[str, P], R]:
    def wrapper(id: str, *args: P.args, **kwargs: P.kwargs) -> R:
        all_ids.append(id)
        return fn(*args, **kwargs)

    return wrapper

@wrap_with_id
def func(x: int, y: int) -> str:
    return f"{x}-{y}"

这时类型检查器只知道第一个参数类型是str,却识别不出参数名为id,签名变成func(str, x: int, y: int) -> str,导致用func(id="abc", x=4, y=5)调用时被误判为错误,但实际调用是合法的。有没有办法添加正确的类型注解,让类型检查器识别到这个参数的名称是id?

解决方案

方法1:使用Protocol定义签名(兼容Python 3.8+)

可以通过定义Protocol来明确装饰后函数的完整签名,包括参数名id,这样类型检查器就能准确识别参数信息。

from typing import ParamSpec, TypeVar, Callable, Protocol

P = ParamSpec("P")
R = TypeVar("R")
all_ids = []

# 定义Protocol描述装饰后的函数签名
class WrappedFunc(Protocol[P, R]):
    def __call__(self, id: str, *args: P.args, **kwargs: P.kwargs) -> R:
        ...

def wrap_with_id(fn: Callable[P, R]) -> WrappedFunc[P, R]:
    def wrapper(id: str, *args: P.args, **kwargs: P.kwargs) -> R:
        all_ids.append(id)
        return fn(*args, **kwargs)

    return wrapper

@wrap_with_id
def func(x: int, y: int) -> str:
    return f"{x}-{y}"

# 此时类型检查器能正确识别id参数,关键字调用不会报错
func(id="abc", x=4, y=5)

方法2:使用PEP 677的参数名语法(Python 3.12+)

如果使用Python 3.12及以上版本,直接利用PEP 677新增的Callable参数名语法,就能简洁地保留参数名信息:

from typing import ParamSpec, TypeVar, Callable, Concatenate

P = ParamSpec("P")
R = TypeVar("R")
all_ids = []

# 用参数名语法定义类型别名
WrappedFunc = Callable[[id: str, *P.args], R]

def wrap_with_id(fn: Callable[P, R]) -> WrappedFunc[P, R]:
    def wrapper(id: str, *args: P.args, **kwargs: P.kwargs) -> R:
        all_ids.append(id)
        return fn(*args, **kwargs)

    return wrapper

@wrap_with_id
def func(x: int, y: int) -> str:
    return f"{x}-{y}"

# 关键字调用合法,类型检查器不会报错
func(id="abc", x=4, y=5)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 08:25:42