Python类型提示:使用*args对函数进行类型提示

3

假设我们希望对一个函数进行类型提示,该函数接受一个位置参数和任意数量的其他参数,例如:

def foo(a: int, *args: Any) -> None:
    for arg in args:
        print(a, isinstance(arg, int))

我们还有另一个函数,它接收 foo 作为参数,因此我们想知道如何将 foo 的类型提示为它本身。将其写成 Callable[[int, Any], None] 是否正确?我感到困惑,因为内部方括号中的类型(即[int, Any])必须与 foo 的参数数量相同--是否必须这样?--但是,显然,*args 可以是任意数量的参数。

1个回答

2

在 Python 3.10 之前,你只能将 foo 类型定义为 Callable[..., None],表示它可以接受任意数量和类型的参数。但是你无法指定至少需要一个 int 参数作为第一个参数。

在 Python 3.10 中,你可以使用 typing.Concatenatetyping.ParamSpec 更精确地匹配类型。

from typing import Concatenate, ParamSpec

P = ParamSpec('P')
def bar(f: Callable[Concatenate[int, P], None]):
    ...

1
但请记住:Python变得如此流行的一个原因是因为它没有静态类型,您可以节省很多时间而不必详细说明所有类型。类型提示是纯粹可选的。如果它们使一切更加复杂,那么您不应该使用它们,并考虑在函数的文档中记录此信息,而不是在类型提示中。 - habrewning
@async 你有没有更复杂的例子说明当没有类型提示时会出现什么情况?对于动态语言来说,静态分析工具有什么好处?你认为所有不可能使用静态类型的功能都可以忽略吗? - habrewning
考虑 def foo(x, y): return x - y。使用动态类型,只有在实际调用函数时才会发现 foo("x", "y") 是一个错误。如果你使用 def foo(x: int, y: int): return x - y 进行提示,则静态类型检查器可以比实际运行代码更有效地标记它为错误。 - chepner
@chepner 但最简单的测试用例就能揭示这一点。我们不能指望静态类型使测试变得过时。永远不要相信你没有至少测试过一次的代码行。在这种情况下,你甚至不需要花费太多精力来找到这个愚蠢的错误。还有一个问题:为什么不能使用浮点数或复数调用 foo?你的类型提示难道不是一个不必要的限制吗? - habrewning
如果需要的话,可以想象类型注释允许Python自动生成“最简单的测试”,而无需显式编写它们。 - chepner
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接