У меня есть функция, которая использует функцию len
для одного из параметров и выполняет итерацию по параметру. Теперь я могу выбрать, нужно ли аннотировать тип с помощью Iterable
или Sized
, но оба дают ошибки в mypy
.
from typing import Sized, Iterable
def foo(some_thing: Iterable):
print(len(some_thing))
for part in some_thing:
print(part)
дает
error: Argument 1 to "len" has incompatible type "Iterable[Any]"; expected "Sized"
В то время как
def foo(some_thing: Sized):
...
дает
error: Iterable expected
error: "Sized" has no attribute "__iter__"
Поскольку нет Intersection
как обсуждалось в этом выпуске, мне нужен какой-то смешанный класс.
from abc import ABCMeta
from typing import Sized, Iterable
class SizedIterable(Sized, Iterable[str], metaclass=ABCMeta):
pass
def foo(some_thing: SizedIterable):
print(len(some_thing))
for part in some_thing:
print(part)
foo(['a', 'b', 'c'])
Это дает ошибку при использовании foo
со list
.
error: Argument 1 to "foo" has incompatible type "List[str]"; expected "SizedIterable"
Это не слишком удивительно, так как:
>>> SizedIterable.__subclasscheck__(list)
False
Поэтому я определил __subclasshook__
(см. Документы).
class SizedIterable(Sized, Iterable[str], metaclass=ABCMeta):
@classmethod
def __subclasshook__(cls, subclass):
return Sized.__subclasscheck__(subclass) and Iterable.__subclasscheck__(subclass)
Тогда проверка подкласса работает:
>>> SizedIterable.__subclasscheck__(list)
True
Но mypy
все еще жалуется на мой list
.
error: Argument 1 to "foo" has incompatible type "List[str]"; expected "SizedIterable"
Как я могу использовать подсказки типов при использовании как функции len
и перебора моего параметра? Я думаю, что приведение типа foo(cast(SizedIterable, ['a', 'b', 'c']))
не является хорошим решением.