MyPy中使用**kwargs传递参数给内部函数的最佳实践(避免冗余类型定义)
MyPy中使用**kwargs传递参数给内部函数的最佳实践(避免冗余类型定义)
我完全懂你的痛点!从R转到Python,这种层层传递关键字参数给内部函数的写法太常用了,结果被mypy报错,确实让人头疼。咱们来一步步拆解问题,找到最省心的解决方案:
为什么mypy会报错?
你原来把**kwargs标注为dict,mypy会把这个类型解析为「键是字符串、值是字典」的结构,但foo函数需要的add_count参数是布尔类型,类型完全不匹配,所以才会抛出那个「参数类型不兼容」的错误。虽然运行时代码没问题,但mypy没法理解你其实是想把kwargs原封不动传给foo。
最佳解决方案:复用目标函数的参数类型
你不想为外部库的函数手动写TypedDict?太合理了!咱们可以直接复用foo函数本身的参数类型定义,完全不用重复劳动。
只需要用到typing模块里的Parameters和Unpack(Python 3.10+可用,旧版本可以用typing_extensions兼容),修改后的代码如下:
from typing import Parameters, Unpack def foo(add_count: bool = False) -> int: return 5 if add_count else 0 def A(x: int, y: int, **kwargs: Unpack[Parameters[foo]]) -> int: return x + y + foo(**kwargs) def B(a: int, b: int, **kwargs: Unpack[Parameters[foo]]) -> int: return a - b + foo(**kwargs) def C(s: int, t: int, **kwargs: Unpack[Parameters[foo]]) -> int: return A(s, t, **kwargs) + B(s, t, **kwargs)
这样改完后,mypy会自动识别出kwargs应该完全匹配foo的关键字参数:
- 合法调用比如
C(5, 6, add_count=True),mypy会通过检查,运行也正常 - 非法调用比如
C(5, 6, add_num=1),mypy会提前报错(而不是等到运行时才抛出TypeError),完美保留了参数校验的能力!
为什么你之前的TypedDict尝试没成功?
你编辑里用了空的TypedDict,这个类型表示「没有任何合法的关键字参数」,所以mypy自然会报错说add_count是意外的参数。空TypedDict完全不适合这个场景,咱们需要的是和foo参数完全匹配的类型,而Parameters[foo]正好帮我们自动生成了这个类型。
备选方案(不推荐但可应急)
如果你的环境没法用Parameters和Unpack,可以临时把**kwargs标注为Any:
def A(x: int, y: int, **kwargs: Any) -> int: return x + y + foo(**kwargs)
但这个方案会失去mypy对无效参数的检查能力,所以只适合临时过渡,最佳实践还是上面的复用参数类型的方法。
备注:内容来源于stack exchange,提问作者Peek




