mypy 应该从联合选项中推断类型吗?

Should mypy infer type from Union options?

在下面的代码中,fn_int 将仅使用 int 作为参数调用,但 mypy 抱怨我可能将其传递给 str(反之亦然fn_str).

我在这里遗漏了什么吗? Should/can 在将参数传递给 fn_intfn_str?

之前,我以某种方式缩小了类型范围
from typing import Union


def fn_int(arg: int) -> None:
    print(arg)


def fn_str(arg: str) -> None:
    print(arg)


def fn(arg: Union[str, int]) -> None:

    if arg in range(4):
        fn_int(arg)
    elif arg == "str":
        fn_str(arg)
    else:
        raise ValueError
> mypy mypy_test.py
mypy_test.py:15: error: Argument 1 to "fn_int" has incompatible type "Union[str, int]"; expected "int"  [arg-type]
mypy_test.py:17: error: Argument 1 to "fn_str" has incompatible type "Union[str, int]"; expected "str"  [arg-type]

arg 是 str 或 int,但你永远不会检查它是哪种类型, 因此检查 if isinstance(arg, int).

然后 mypy 知道这个函数只有在它是所需类型时才会被调用。

示例

if isinstance(arg, int):
    fn_int(arg)
elif isinstance(arg, str):
    fn_str(arg)
else:
    raise ValueError

您要查找的内容称为 type narrowing,而 mypy 不会对 ==in 比较进行类型缩小。毕竟,它们并不能真正保证类型。作为一个相当常见的示例,在 x == 3x in range(5) 之后,x 仍然可以是浮点数而不是整数。 (当然,标准字符串不会通过 in range(4) 检查,标准 int 也不会通过 == "str" 检查,但你可以有一个奇怪的子类。)

mypy 将对以下类型的表达式进行类型缩小:

  • isinstance(x, SomeClass)x 缩小为 SomeClass
  • issubclass(x, SomeClass)x 缩小为 Type[SomeClass]
  • type(x) is SomeClassx 缩小为 SomeClass
  • callable(x)x 缩小为可调用类型。

你也可以用 typing.TypeGuard 编写你自己的类型保护,mypy 也会理解这些。