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

如何编写通过mypy严格检查的Python去首参装饰器

实现类型安全的“丢弃首个参数”装饰器

问题根源

你当前的代码框架逻辑是对的,但args[1:]会被mypy推断为泛型tuple[object, ...],无法和P.args的具体类型匹配,这才触发了类型错误。要解决这个问题,需要让类型检查器明确知道切片后的参数元组类型就是P.args。

修复后的代码

通过类型断言可以直接告诉mypy切片结果的类型,同时保留原有的类型结构:

from functools import wraps
from typing import Any, Callable, Concatenate, ParamSpec, TypeVar, cast

P = ParamSpec('P')
R = TypeVar('R')

def drop_first_arg(f: Callable[Concatenate[Any, P], R]) -> Callable[P, R]:
    @wraps(f)
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
        # 用cast断言切片后的参数完全匹配P.args的类型
        return f(*cast(P.args, args[1:]), **kwargs)
    return wrapper

验证用例

下面的代码可以验证装饰器的类型正确性和功能:

# 原函数:首个参数为占位符(比如类方法的self),后续是业务参数
def demo_func(placeholder: object, x: int, y: str, z: bool = False) -> str:
    return f"{x}-{y}-{z}"

# 应用装饰器
trimmed_func = drop_first_arg(demo_func)

# 正确调用:mypy会自动提示参数类型
print(trimmed_func(10, "hello"))  # 输出 "10-hello-False"
print(trimmed_func(20, "world", z=True))  # 输出 "20-world-True"

# 错误调用会触发mypy报错
# trimmed_func("not_int", "oops")  # mypy: Argument 1 has incompatible type "str"; expected "int"

关键说明

  • cast(P.args, args[1:]):这是核心修复——因为mypy无法自动推导args[1:]的具体类型,我们用cast明确告知它切片结果和P.args完全一致。
  • Concatenate[Any, P]:准确描述原函数的参数结构:第一个参数是任意类型,后续参数遵循P的参数规格。
  • Callable[P, R]:装饰后的函数只暴露P定义的参数,返回类型与原函数保持一致。

修改后,运行mypy --strict将不会再出现类型错误,同时完整保留了原函数的参数类型提示。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 23:05:12