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

如何为注入default参数的装饰器修正类型提示消除mypy报错

问题

我想要编写一个装饰器,用于捕获被装饰函数抛出的异常并返回默认值。目前已完成实现,但在调用被装饰函数时传入default关键字参数会触发mypy错误:error: Unexpected keyword argument "default" for "foo" [call-arg]。我希望通过类型提示将注入的default参数添加到被装饰方法的签名中,同时保留两种设置默认值的方式:既可以在装饰器上设置default参数,也可以在调用被装饰函数时传入default关键字参数(便于单元测试)。

当前装饰器及使用示例代码如下:

import functools
from typing import TypeVar, ParamSpec, Callable

P = ParamSpec("P")
R = TypeVar("R")


def suppress(
    default: object = None,
) -> Callable[[Callable[P, R]], Callable[P, R | object]]:

    def decorator_suppress(func: Callable[P, R]) -> Callable[P, R | object]:
        functools.wraps(
            func,
        )

        def wrapper_suppress(
            *args: P.args,
            default: object = default,
            **kwargs: P.kwargs,
        ) -> R | object:

            try:
                return func(*args, **kwargs)
            except Exception as e:
                return default

        return wrapper_suppress

    return decorator_suppress

使用示例:

@suppress(default="foo!")
def foo(a: int, b: int) -> int:
    return a + b


a = foo(1, 1)
b = foo(1, "2")
c = foo(1, "2", default="bar!")

print(a)
print(b)
print(c)

运行结果:

2
foo!
bar!
解决方案

要解决mypy的参数报错问题,需要通过类型系统明确告知被装饰后的函数支持新增的default参数。核心是利用ParamSpec的Concatenate功能合并原函数参数与新增参数,同时通过重载确保类型提示的精确性。

修正后的装饰器代码如下:

import functools
from typing import TypeVar, ParamSpec, Callable, Concatenate, overload

P = ParamSpec("P")
R = TypeVar("R")
D = TypeVar("D")


@overload
def suppress(default: D) -> Callable[[Callable[P, R]], Callable[Concatenate[P.args, P.kwargs, {"default": D}], R | D]]:
    ...


@overload
def suppress() -> Callable[[Callable[P, R]], Callable[Concatenate[P.args, P.kwargs, {"default": object | None}], R | object | None]]:
    ...


def suppress(
    default: object | None = None,
) -> Callable[[Callable[P, R]], Callable]:
    def decorator_suppress(func: Callable[P, R]) -> Callable:
        @functools.wraps(func)
        def wrapper_suppress(
            *args: P.args,
            default: object | None = default,
            **kwargs: P.kwargs,
        ) -> R | object | None:
            try:
                return func(*args, **kwargs)
            except Exception:
                return default

        return wrapper_suppress

    return decorator_suppress

关键修改点:

  • 参数签名合并:通过Concatenate将原函数的参数列表与新增的default关键字参数合并,让mypy识别到被装饰函数的新签名。
  • 类型变量D:专门捕获装饰器传入的default参数类型,保证返回值类型为原函数返回类型R与D的联合类型,类型提示更精准。
  • 重载装饰器:用@overload分别定义传入default和不传入的两种情况,覆盖不同使用场景的类型检查需求。
  • 修正返回类型:装饰器返回的函数类型明确包含新增的default参数,彻底解决Unexpected keyword argument错误。

修正后,原示例中的两种默认值设置方式都能正常通过mypy检查:

  • 装饰器层面预设默认值:@suppress(default="foo!")
  • 调用时临时指定默认值:foo(1, "2", default="bar!")

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 11:14:53