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

Python中如何修改带参装饰器支持任意数量泛型序列并保留类型提示?

实现支持任意泛型参数序列的装饰器并保留类型提示

要让deco_with_params支持任意数量的泛型类型序列,同时让mypy能正确校验类型匹配,核心是利用PEP 646的TypeVarTuple和Unpack处理可变长度的类型参数,结合泛型类实现装饰器。

完整实现代码

from collections.abc import Callable, Sequence
from typing import TypeVarTuple, Unpack, Generic

# 定义可变长度的类型元组,绑定被装饰函数的参数类型序列
Ts = TypeVarTuple('Ts')

class Decorator(Generic[Unpack[Ts]]):
    def __init__(self, **kwargs: Sequence[Unpack[Ts]]):
        self.param_sequences = kwargs

    def __call__(self, func: Callable[[Unpack[Ts]], None]) -> None:
        # 按函数参数顺序提取对应序列的第一个元素(对应原代码的shortcut逻辑)
        args = [
            self.param_sequences[param_name][0]
            for param_name in func.__code__.co_varnames[:func.__code__.co_argcount]
        ]
        func(*args)

# 导出装饰器
deco_with_params = Decorator

关键逻辑说明

  1. 可变类型绑定:TypeVarTuple('Ts')定义了可变长度的类型集合,用来指代被装饰函数的所有参数类型;Generic[Unpack[Ts]]让装饰器类与这个类型集合绑定。
  2. 参数类型约束:**kwargs: Sequence[Unpack[Ts]]要求装饰器的每个关键字参数必须是对应类型的序列,序列元素类型与Ts中的类型一一对应。
  3. 类型校验匹配:__call__方法只接收参数类型为Unpack[Ts]的函数,确保函数参数类型与装饰器参数的序列元素类型完全匹配。
  4. 参数传递逻辑:通过func.__code__.co_varnames获取函数的参数名称,按顺序从装饰器的参数序列中取第一个元素传递给函数,保持原代码的shortcut行为。

用法验证

合法用法(mypy接受)

@deco_with_params(string=["one", "two", "three"], number=[4., 5.1, 6.7], extra=[True, False])
def foo(string: str, number: float, extra: bool) -> None:
    print(string, number, extra)

非法用法(mypy拒绝)

@deco_with_params(string=["one", "two", "three"], number=[True, False], extra=[4., 5.1, 6.7])
def bar(string: str, number: float, extra: bool) -> None:
    print(string, number, extra)

mypy会提示number参数的序列元素类型bool与函数参数类型float不匹配,extra参数的序列元素类型float与函数参数类型bool不匹配,符合预期校验逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 09:25:35