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

使用Callable标注函数时如何保留关键字参数并修复类型错误?

如何修复带stringify选项的装饰器的mypy类型错误

问题背景

我需要给多个函数添加stringify选项:当stringify=True时,函数返回str(result)而非原本的result。为避免重复实现逻辑,我使用了装饰器,但代码运行正常的情况下,mypy报出类型错误,同时丢失关键字参数自动补全:

  • Unexpected keyword argument "value" for "func1"mypy(error)
  • Unexpected keyword argument "stringify" for "func1"mypy(error)

我的简化代码如下:

from typing import Callable, Union


def WithStringifyOption(func: Callable[[int], int]) -> Callable[[int, bool], Union[int, str]]:
    def wrapper(value: int, stringify: bool = False) -> Union[int, str]:
        result = func(value)
        return str(result) if stringify else result
    return wrapper

# 原函数
def func0(value: int) -> int:
    return value * 2

# 装饰后的函数
@WithStringifyOption
def func1(value: int) -> int:
    return value * 2


func0(value=2)
func1(value=3, stringify=True)

根据typing.Callable官方文档:

没有语法用于标注可选参数或关键字参数;这类函数类型很少用作回调类型。

注:这是实际项目的简化示例,我需要为每个函数添加更复杂的逻辑,因此无法重构所有函数重复实现该逻辑,也不想大量添加type: ignore,更不想替换关键字参数为位置参数。

解决方案

使用Python 3.10+引入的typing.ParamSpec和typing.TypeVar来精确标注装饰器的参数和返回值类型,让mypy能正确识别关键字参数和可选参数。

修改后的代码

from typing import Callable, ParamSpec, TypeVar, Union

# 定义参数规格和返回值类型变量
P = ParamSpec("P")
R = TypeVar("R")

def WithStringifyOption(func: Callable[P, R]) -> Callable[P + (bool,), Union[R, str]]:
    def wrapper(*args: P.args, stringify: bool = False, **kwargs: P.kwargs) -> Union[R, str]:
        result = func(*args, **kwargs)
        return str(result) if stringify else result
    return wrapper

# 原函数
def func0(value: int) -> int:
    return value * 2

# 装饰后的函数
@WithStringifyOption
def func1(value: int) -> int:
    return value * 2

# 测试调用,mypy不再报错,关键字参数补全正常
func0(value=2)
func1(value=3, stringify=True)
func1(3, stringify=True)  # 位置参数也支持

关键说明

  • ParamSpec("P"):捕获原函数所有参数(包括位置参数、关键字参数)的类型信息。
  • TypeVar("R"):捕获原函数的返回值类型。
  • Callable[P + (bool,), Union[R, str]]:表示装饰后的函数参数是原函数参数加上可选的stringify布尔参数,返回值为原返回值或字符串类型。
  • 装饰器内部的wrapper用*args: P.args和**kwargs: P.kwargs接收原函数的所有参数,确保关键字参数能被正确传递和识别。

如果需要支持Python 3.8+版本,可以使用typing_extensions库中的ParamSpec和TypeVar,用法完全一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 13:27:17