I am trying to implement a generator that will return a pair of a sequence element and a boolean value indicating whether the element is the last one.
from collections.abc import Generator, Iterable
from itertools import chain, tee
from typing import TypeVar
_T1 = TypeVar('_T1')
_MISSING = object()
def pairwise(iterable: Iterable[_T1]) -> Iterable[tuple[_T1, _T1]]:
# See https://docs.python.org/3.9/library/itertools.html#itertools-recipes
a, b = tee(iterable)
next(b, None)
return zip(a, b)
def annotated_last(sequence: Iterable[_T1]) -> Generator[tuple[_T1, bool], Any, None]:
for current_item, next_item in pairwise(chain(sequence, [_MISSING])):
is_last = next_item is _MISSING
yield current_item, is_last # <-- mypy error
However, mypy
returns this error:
Incompatible types in "yield" (actual type "tuple[object, bool]", expected type "tuple[_T1, bool]")
Tell me how to correctly annotate types in these functions.
I am using Python version 3.9.19
This is because you created a chain
iterator like so: chain(sequence, [_MISSING])
, and the type inference has to infer the most generic type from these arguments, but _MISSING
is object, so it has to be an iterator of object
. Note, you can implement the function you want with the signature you want straightforwardly (albeit, less elegantly) doing something like:
from collections.abc import Iterator, Iterable
from typing import TypeVar
_T1 = TypeVar('_T1')
def annotated_last(sequence: Iterable[_T1]) -> Iterator[tuple[_T1, bool]]:
it = iter(sequence)
try:
previous = next(it)
except StopIteration:
return
for current in it:
yield previous, False
previous = current
yield previous, True