The following code:
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)
generates a mypy error: Argument 1 to "act" has incompatible type "object"; expected "Union[str, int]"
However when removing the enumerate function the typing is fine:
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)
Does anyone know what the enumerate function is doing to effect the types? This is python 3.9 and mypy 0.921
enumerate.__next__
needs more context than is available to have a return type more specific than Tuple[int, Any]
, so I believe mypy
itself would need to be modified to make the inference that enumerate(actions)
produces Tuple[int,Union[str,int]]
values.
Until that happens, you can explicitly cast the value of action
before passing it to 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)
You can also make process
generic (which now that I've thought of it, is probably a better idea, as it avoids the overhead of calling cast
at runtime).
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)
Here, T
is not a union of types, but a single concrete type whose identity is fixed by the call to process
. Iterable[T]
is either Iterable[str]
or Iterable[int]
, depending on which type you pass to process
. That fixes T
for the rest of the call to process
, which every call to act
must take the same type of argument.
An Iterable[str]
or an Iterable[int]
is a valid argument, binding T
to int
or str
in the process. Now enumerate.__next__
apparently can have a specific return type Tuple[int, T]
.