329

我正在尝试使用抽象基类来编写 Python 的类型注释来编写一些接口。有没有办法注释和的可能*args类型**kwargs

例如,如何表示函数的合理参数是一个int或两个ints?type(args)给出了Tuple所以我的猜测是将类型注释为Union[Tuple[int, int], Tuple[int]],但这不起作用。

from typing import Union, Tuple

def foo(*args: Union[Tuple[int, int], Tuple[int]]):
    try:
        i, j = args
        return i + j
    except ValueError:
        assert len(args) == 1
        i = args[0]
        return i

# ok
print(foo((1,)))
print(foo((1, 2)))
# mypy does not like this
print(foo(1))
print(foo(1, 2))

来自 mypy 的错误消息:

t.py: note: In function "foo":
t.py:6: error: Unsupported operand types for + ("tuple" and "Union[Tuple[int, int], Tuple[int]]")
t.py: note: At top level:
t.py:12: error: Argument 1 to "foo" has incompatible type "int"; expected "Union[Tuple[int, int], Tuple[int]]"
t.py:14: error: Argument 1 to "foo" has incompatible type "int"; expected "Union[Tuple[int, int], Tuple[int]]"
t.py:15: error: Argument 1 to "foo" has incompatible type "int"; expected "Union[Tuple[int, int], Tuple[int]]"
t.py:15: error: Argument 2 to "foo" has incompatible type "int"; expected "Union[Tuple[int, int], Tuple[int]]"

mypy 不喜欢函数调用是有道理的,因为它希望tuple调用本身有 a 。解包后的添加也给出了我不明白的打字错误。

如何注释 和 的合理*args类型**kwargs

4

6 回答 6

342

对于可变位置参数 ( *args) 和可变关键字参数 ( **kw),您只需指定一个此类参数的预期值。

类型提示PEP的任意参数列表和默认参数值部分:

任意参数列表也可以进行类型注释,以便定义:

def foo(*args: str, **kwds: int): ...

是可以接受的,这意味着,例如,以下所有都表示具有有效参数类型的函数调用:

foo('a', 'b', 'c')
foo(x=1, y=2)
foo('', z=0)

所以你想像这样指定你的方法:

def foo(*args: int):

但是,如果您的函数只能接受一个或两个整数值,则根本不应该使用*args,使用一个显式位置参数和第二个关键字参数:

def foo(first: int, second: Optional[int] = None):

现在您的函数实际上仅限于一个或两个参数,并且如果指定,两者都必须是整数。*args 总是意味着 0 或更多,并且不能被类型提示限制到更具体的范围。

于 2016-05-04T15:29:38.697 回答
44

正确的方法是使用@overload

from typing import overload

@overload
def foo(arg1: int, arg2: int) -> int:
    ...

@overload
def foo(arg: int) -> int:
    ...

def foo(*args):
    try:
        i, j = args
        return i + j
    except ValueError:
        assert len(args) == 1
        i = args[0]
        return i

print(foo(1))
print(foo(1, 2))

请注意,您不会@overload在实际实现中添加或键入注释,这必须放在最后。

你需要一个新版本的typingmypy 来获得对存根文件之外的@overload 的支持。

您还可以使用它来改变返回的结果,以明确哪些参数类型对应于哪个返回类型。例如:

from typing import Tuple, overload

@overload
def foo(arg1: int, arg2: int) -> Tuple[int, int]:
    ...

@overload
def foo(arg: int) -> int:
    ...

def foo(*args):
    try:
        i, j = args
        return j, i
    except ValueError:
        assert len(args) == 1
        i = args[0]
        return i

print(foo(1))
print(foo(1, 2))
于 2017-08-24T18:56:04.910 回答
27

作为对上一个答案的简短补充,如果您尝试在 Python 2 文件上使用 mypy 并且需要使用注释来添加类型而不是注释,则需要分别为类型添加前缀forargskwargswith :***

def foo(param, *args, **kwargs):
    # type: (bool, *str, **int) -> None
    pass

这被 mypy 视为与以下 Python 3.5 版本相同foo

def foo(param: bool, *args: str, **kwargs: int) -> None:
    pass
于 2016-06-28T04:15:02.137 回答
26

还没有真正支持

虽然您可以使用类型注释可变参数,但我觉得它不是很有用,因为它假定所有参数都是相同的类型。

mypy 尚不支持允许单独指定每个可变参数的正确类型注释*args和。**kwargs有一个Expand在模块上添加助手的建议mypy_extensions,它的工作方式如下:

class Options(TypedDict):
    timeout: int
    alternative: str
    on_error: Callable[[int], None]
    on_timeout: Callable[[], None]
    ...

def fun(x: int, *, **options: Expand[Options]) -> None:
    ...

GitHub 问题于 2018 年 1 月开放,但仍未关闭。请注意,虽然问题是 about **kwargs,但Expand语法也可能用于*args

于 2020-08-23T18:37:53.683 回答
4

在某些情况下,**kwargs 的内容可以是多种类型。

这似乎对我有用:

from typing import Any

def testfunc(**kwargs: Any) -> None:
    print(kwargs)

或者

from typing import Any, Optional

def testfunc(**kwargs: Optional[Any]) -> None:
    print(kwargs)

如果您觉得需要限制类型,**kwargs我建议创建一个类似结构的对象并在那里添加类型。这可以通过数据类或 pydantic 来完成。

from dataclasses import dataclass

@dataclass
class MyTypedKwargs:
   expected_variable: str
   other_expected_variable: int


def testfunc(expectedargs: MyTypedKwargs) -> None:
    pass
于 2021-03-26T05:07:47.623 回答
2

如果想描述 kwargs 中预期的特定命名参数,则可以传入 TypedDict(定义必需和可选参数)。可选参数是 kwargs。注意:TypedDict在 python >= 3.8 中参见这个例子:

import typing

class RequiredProps(typing.TypedDict):
    # all of these must be present
    a: int
    b: str

class OptionalProps(typing.TypedDict, total=False):
    # these can be included or they can be omitted
    c: int
    d: int

class ReqAndOptional(RequiredProps, OptionalProps):
    pass

def hi(req_and_optional: ReqAndOptional):
    print(req_and_optional)
于 2021-03-04T22:52:47.450 回答