枚举导致 mypy 类型不兼容错误

Jam*_*rts 5 python typing enumerate mypy

下面的代码:

from typing import Union


def process(actions: Union[list[str], list[int]]) -> None:
    for pos, action in enumerate(actions):
        act(action)


def act(action: Union[str, int]) -> None:
    print(action)
Run Code Online (Sandbox Code Playgroud)

生成 mypy 错误: Argument 1 to "act" has incompatible type "object"; expected "Union[str, int]"

但是,当删除枚举函数时,输入就可以了:

from typing import Union


def process(actions: Union[list[str], list[int]]) -> None:
    for action in actions:
        act(action)


def act(action: Union[str, int]) -> None:
    print(action)
Run Code Online (Sandbox Code Playgroud)

有谁知道枚举函数正在做什么来影响类型?这是 python 3.9 和 mypy 0.921

che*_*ner 4

enumerate.__next__需要比可用的上下文更多的上下文才能获得比 更具体的返回类型Tuple[int, Any],因此我认为mypy需要对其本身进行修改才能进行enumerate(actions)产生Tuple[int,Union[str,int]]值的推理。

在此之前,您可以在将 的值传递action给 之前显式转换它的值act

from typing import Union, cast

StrOrInt = Union[str, int]

def process(actions: Union[list[str], list[int]]) -> None:
    for pos, action in enumerate(actions):
        act(cast(StrOrInt, action))


def act(action: Union[str, int]) -> None:
    print(action)
Run Code Online (Sandbox Code Playgroud)

您还可以制作process通用的(现在我已经想到了,这可能是一个更好的主意,因为它避免了cast运行时调用的开销)。

from typing import Union, cast, Iterable, TypeVar

T = TypeVar("T", str, int)

def process(actions: Iterable[T]) -> None:
    for pos, action in enumerate(actions):
        act(action)


def act(action: T) -> None:
    print(action)
Run Code Online (Sandbox Code Playgroud)

这里,T不是类型的联合,而是单个具体类型,其标识通过调用来固定processIterable[T]是 或Iterable[str]Iterable[int]具体取决于您传递给的类型process。这修复了T对 的其余调用process,每次调用都act必须采用相同类型的参数。

AnIterable[str]或 anIterable[int]是有效的参数,绑定T到进程intstr在进程中。现在enumerate.__next__显然可以有一个特定的返回类型Tuple[int, T]