我目前正在尝试学习如何最好地在我的 Python 代码中包含类型,但我对如何在我的函数签名中正确键入
**kwargs
感到有点困惑。
我目前拥有的一个最小例子:
def foo(name: str, **kwargs: Dict[str, Any]) -> int:
modified_name: str = f'This is an example for {name}!'
return some_other_func(modified_name, **kwargs)
def bar(id_number: int, **kwargs: Dict[str, Any]) -> int:
name: str = some_func(id_number)
return foo(name, number=id_number, **kwargs)
我期望发生什么:由于
foo
没有指定number
关键字参数,我希望它被收集到foo
的kwargs
中,这本质上是一个Dict[str, Any]
。由于“int”是“Any”类型,我应该不会对打字有任何抱怨。
真正发生的事情:在运行时,
number
被收集到 foo
的 kwarg
s 和 some_other_func
可以安全地提取 number
关键字参数。然而,mypy(版本 1.1.1)抱怨如下
error: Argument "number" to "foo" has incompatible type "int"; expected "Dict[str, Any]"
pyright(版本 1.1.292)的类似投诉:
Argument of type "int" cannot be assigned to parameter "number" of type "Dict[str, Any]" in function "foo" "int" is incompatible with "Dict[str, Any]"
这是预期的行为吗?如果我传递直接被调用者在函数签名中没有的关键字参数,我是否应该期望类型检查器抱怨?
继续这个我想好吧,我将直接在
number
中添加kwargs
作为键(我不确定这是否是好的做法,但这不是重点),所以这样我肯定会通过一个Dict[str, Any]
。这基本上是我尝试过的:
def bar(id_number: int, **kwargs: Dict[str, Any]) -> int:
name: str = some_func(id_number)
kwargs[number] = id_number
return foo(name, **kwargs)
但是现在我得到错误(来自 mypy):
error: Incompatible types in assignment (expression has type "int", target has type "Dict[str, Any]"
类似来自pyright:
Argument of type "int" cannot be assigned to parameter "__value" of type "Dict[str, Any]" in function "__setitem__" "int" is incompatible with "Dict[str, Any]"
但这更令人困惑,为什么他们似乎暗示我正在分配给
kwargs
,而实际上我正在分配给kwargs
中的一个键?
这是否意味着我应该在 inside 的
kwargs
中输入值?
如果我输入
kwargs
作为“Union[int, Dict[str, Any]]”,我没有遇到任何问题。所以这似乎证实了这个问题。但是为什么会有不同的行为呢?我们应该知道 kwargs
是一个映射,所以避免额外的打字换行,这仅仅是为了方便吗?但我在任何地方都看不到这一点,我也不是 100% 相信我对它的工作原理是正确的。
最后,我也尝试过使用
Mapping[str, Any]
而不是Dict[str, Any]
,但它有同样的问题。
tl;dr:
kwargs
是一个包含函数签名未指定的关键字的字典,它可以像 Dict[str, Any]
一样访问和修改。为什么我不能像一个一样打字?我应该怎么打字?