Many functions in stdlib that are documented to accept an "iterable" are annotated as Iterable[T]. This means the arguments must have __iter__. However, the actual functions don't require the arguments to have __iter__, they also work with arguments that have only __getitem__. That's because internally they use iter (in Python) or PySequence_Fast (in C).
As the docs for both iter and the PySequence_* functions show, these functions support two protocols for the argument: the "iterable" protocol (__iter__) and the "sequence" protocol (__getitem__).
Example functions that have this bug right now:
enumerate
str.join
bytes.__new__
bytes.__join
For enumerate, a bug was actually filed on the mypy repo but in fact I believe this should be corrected in typeshed.
Aside: for loops also similarly accept "iterables" that have only __getitem__, but that is implemented inside type checkers themselves. For now mypy still doesn't support it properly, but Pyright does.
Many functions in stdlib that are documented to accept an "iterable" are annotated as
Iterable[T]
. This means the arguments must have__iter__
. However, the actual functions don't require the arguments to have__iter__
, they also work with arguments that have only__getitem__
. That's because internally they useiter
(in Python) or PySequence_Fast (in C).As the docs for both
iter
and thePySequence_*
functions show, these functions support two protocols for the argument: the "iterable" protocol (__iter__
) and the "sequence" protocol (__getitem__
).Example functions that have this bug right now:
For enumerate, a bug was actually filed on the mypy repo but in fact I believe this should be corrected in typeshed.
Aside:
for
loops also similarly accept "iterables" that have only__getitem__
, but that is implemented inside type checkers themselves. For now mypy still doesn't support it properly, but Pyright does.