当我开始写类型提示时,我对Python的可变参数运算符*
和**
(通常称为*args
和**kwargs
)的使用也感到有些困惑。以下是我总结出的内容。
回想一下,*
运算符用于捕获可变的位置参数并将其放入一个元组中,而**
则用于捕获可变的关键字参数并将其放入一个字典中。例如,考虑以下函数:
def variable(*args, **kwargs):
...
在函数体内,args
将是一个元组,而kwargs
将是一个带有字符串键的字典。
在添加类型提示时,似乎自然而然地尝试声明args
和kwargs
的完整类型。如果我们希望所有的值都是int
,我们可能会尝试:
def variable(*args: tuple[int, ...], **kwargs: dict[str, int]) -> None:
...
(元组定义中的...
表示可以是任意长度的元组。)
但这是不正确的。我们可以通过添加一个调用来检查:
variable(1, 2, 3, a=4, b=5, c=6)
在文件上运行Mypy时,它会发现每个参数都有问题:
$ mypy example.py
example.py:5: error: Argument 1 to "variable" has incompatible type "int"; expected "Tuple[int, ...]"
example.py:5: error: Argument 2 to "variable" has incompatible type "int"; expected "Tuple[int, ...]"
example.py:5: error: Argument 3 to "variable" has incompatible type "int"; expected "Tuple[int, ...]"
example.py:5: error: Argument "a" to "variable" has incompatible type "int"; expected "Dict[str, int]"
example.py:5: error: Argument "b" to "variable" has incompatible type "int"; expected "Dict[str, int]"
example.py:5: error: Argument "c" to "variable" has incompatible type "int"; expected "Dict[str, int]"
Found 6 errors in 1 file (checked 1 source file)
那正确的方式是什么呢?
- 单个
*
始终绑定到一个元组,而**
始终绑定到一个带有字符串键的字典。由于这个限制,类型提示只需要您定义所包含参数的类型。类型检查器会自动添加tuple[_, ...]
和dict[str, _]
这些容器类型。
引入类型提示的Python增强提案(PEP 484)指定了这个规则:
任意参数列表也可以进行类型注释,因此以下定义是可以接受的:
def foo(*args: str, **kwds: int): ...
在函数
foo
的主体内,变量args
的类型被推断为Tuple[str, ...]
,而变量kwds
的类型为Dict[str, int]
。
因此,我们可以将我们的函数正确地类型提示为:
def variable(*args: str, **kwargs: int) -> None:
...
这样就可以通过类型检查了。