Мне трудно понять, почему mypy выдает ошибку в следующем примере.
import numpy as np
from typing import Sequence
def compute(x: Sequence[float]) -> bool:
# some computation that does not modify x
...
compute(np.linspace(0, 1, 10))
Ошибка MyPy:
Argument 1 to "compute" has incompatible type "ndarray[Any, dtype[floating[Any]]]"; expected "Sequence[float]" [arg-type]
В частности, поскольку typing.Sequence требует итерируемости, обратимости и индексации, я думаю, что массив numpy также должен быть Sequence. Связано ли это с тем, что массивы numpy изменяемы, а типы Sequence должны быть неизменяемыми? Я заметил, что когда я меняю Sequence на Iterable, проблема исправлена. Но мне нужно иметь возможность индексировать x в compute.
Итак, как лучше всего указать тип функции compute, чтобы она могла принимать объекты с итерируемостью и индексацией?
почему бы вам просто не создать свой собственный протокол? тот, который требует __iter__ и __getitem__?
@juanpa.arrivillaga ваш первый комментарий вводит в заблуждение: list совместим с Sequence и не является неизменным. Реализация не обязательно должна быть неизменной — просто гарантируется наличие немодифицирующих методов. Ничто не мешает вам добавлять модифицирующие методы, они просто невидимы/непригодны для использования в качестве членов Sequence. Только index и count мешают здесь.
@SUTerliakov да, вы абсолютно правы, если мы говорим о аннотациях типов






Попробуйте использовать np.ndarray в качестве подсказки типа для x. Я проверил type(np.linspace(0, 1, 10)), и он вернул np.ndarray
Однако в других местах я хотел бы иметь возможность вызывать compute для переменных, которые не являются массивами numpy.
np.ndarray не является Sequence (несовместимым с соответствующим протоколом), потому что np.ndarray не реализует методы .count и .index, которые необходимы для collections.abc.Sequence, см. таблицу методов по ссылке.
Вот актуальный вопрос.
Чтобы сделать что-то похожее на последовательность, совместимое с np.ndarray, я предлагаю определить ваш протокол:
from __future__ import annotations
from collections.abc import Collection, Reversible, Iterator
from typing import Protocol, TypeVar, overload
_T_co = TypeVar('_T_co', covariant=True)
class WeakSequence(Collection[_T_co], Reversible[_T_co], Protocol[_T_co]):
@overload
def __getitem__(self, index: int) -> _T_co: ...
@overload
def __getitem__(self, index: slice) -> WeakSequence[_T_co]: ...
def __contains__(self, value: object) -> bool: ...
def __iter__(self) -> Iterator[_T_co]: ...
def __reversed__(self) -> Iterator[_T_co]: ...
Это в основном то же самое, что и Sequence определение в typeshed, за исключением того, что я использую Protocol вместо Generic (чтобы избежать добавления реализации - заглушки не нужны, плюс это делает структурные подтипы более очевидными) и опускаю методы .index и .count .
Теперь np.ndarray должен быть совместим с WeakSequence, а также с list или tuple, и вы можете использовать WeakSequence вместо collections.abc.Sequence в своих аннотациях.
Ах, хорошо, я пропустил, что у него должны быть методы .count и .index. Я думаю, что index просто означает, что он поддерживает индексацию. Думаю, я тоже пропустил требование подсчета.
Потому что
numpy.ndarrayобъекты не соответствуютcollections.abc.SequenceABC, который должен быть доступен только для чтения, и не поддерживает.indexи.count, которые являются частью интерфейса.