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

如何在Python中避免重复传递关键字参数并保留类型提示?

Python 复用函数参数类型提示(类似TypeScript Parameters)

问题场景

我有一个类的公开方法train_model,内部需要调用另一个签名完全一致的self.model.train方法,目前只能手动逐个传递所有参数,非常繁琐。但因为是公开方法必须保留完整的类型提示,所以不想直接用**kwargs跳过类型检查。

原方法代码:

def train_model(self, out_dir='./out/',
                test_size=0.2, train_size=None,
                random_state=None, shuffle=True, stratify=None,
                epochs=DEFAULT_EPOCHS, batch_size=DEFAULT_BATCH_SIZE):
    ...
    # 手动传参繁琐且易出错
    self.model.train(test_size=test_size, train_size=train_size, random_state=random_state, shuffle=shuffle, stratify=stratify, epochs=epochs, batch_size=batch_size)

我想要实现类似TypeScript中Parameters工具类型的效果,能直接复用目标函数的参数类型,比如TS里的写法:

function sum(a: number, b: number) {
    return a + b;
}

type SumParamsType = Parameters<typeof sum>

// 后续可在其他地方复用该参数类型

我试过的两种失败方案

方案1:用TypeVar捕获参数类型

from typing import TypeVar
T = TypeVar('T')

def f1(a=1, b=2, c=3):
    return a+b+c

# 试图用TypeVar捕获f1的参数类型,但无法实现
def f2(z=0, **kwargs: T):
    return z+f1(**kwargs)

# 结果:T无法识别f1的具体参数类型,类型检查完全失效

方案2:直接读取函数annotations

def f1(a=1, b=2, c=3):
    return a+b+c

def f2(z=0, **kwargs: f1.__annotations__['kwargs']):
    return z + f1(**kwargs)

# 结果:kwargs被识别为Any类型,完全失去类型提示作用

可行解决方案

方法1:使用ParamSpec(推荐,Python 3.10+)

Python 3.10引入了typing.ParamSpec,专门用来捕获函数的参数类型规格,完美解决这个问题。低版本Python可以安装typing_extensions库兼容该特性。

示例代码:

from typing import ParamSpec, TypeVar
from typing_extensions import Self  # Python 3.11+可直接用typing.Self

# 定义参数规格变量和返回值类型变量
P = ParamSpec('P')
DEFAULT_EPOCHS = 10
DEFAULT_BATCH_SIZE = 32

class Model:
    def train(self, test_size: float = 0.2, train_size: float | None = None,
              random_state: int | None = None, shuffle: bool = True, stratify: any | None = None,
              epochs: int = DEFAULT_EPOCHS, batch_size: int = DEFAULT_BATCH_SIZE) -> None:
        # 模型训练逻辑
        pass

class Trainer:
    def __init__(self):
        self.model = Model()
    
    def train_model(self, out_dir: str = './out/', **kwargs: P.args) -> Self:
        # 处理out_dir相关逻辑
        ...
        # 直接传递参数,类型检查工具会自动校验是否符合train方法的签名
        self.model.train(**kwargs)
        return self

这样写的话,train_model的**kwargs会完全继承Model.train的参数类型提示,调用时传入不符合的参数会被mypy、Pyright等工具报错,同时无需手动维护重复的参数列表。

方法2:用TypedDict定义参数类型

如果不想依赖ParamSpec,可以手动把目标函数的参数定义成TypedDict,再在两个函数中复用这个字典类型:

from typing import TypedDict, Optional
DEFAULT_EPOCHS = 10
DEFAULT_BATCH_SIZE = 32

# total=False表示所有字段都是可选的
class TrainParams(TypedDict, total=False):
    test_size: float
    train_size: Optional[float]
    random_state: Optional[int]
    shuffle: bool
    stratify: Optional[any]
    epochs: int
    batch_size: int

class Model:
    def train(self, test_size: float = 0.2, train_size: Optional[float] = None,
              random_state: Optional[int] = None, shuffle: bool = True, stratify: Optional[any] = None,
              epochs: int = DEFAULT_EPOCHS, batch_size: int = DEFAULT_BATCH_SIZE) -> None:
        pass

class Trainer:
    def __init__(self):
        self.model = Model()
    
    def train_model(self, out_dir: str = './out/', **kwargs: TrainParams) -> None:
        ...
        self.model.train(**kwargs)

这种方式需要手动维护TypedDict和函数签名的一致性,适合参数列表不常变动的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 05:45:30