Могут ли обобщенные выражения mypy передавать тип возвращаемой последовательности в качестве аргумента? - PullRequest
0 голосов
/ 02 ноября 2019

Я хотел бы написать следующий универсальный код Python:

from itertools import chain
from typing import Sequence, Hashable, List, Tuple, Type, TypeVar


SequenceT = TypeVar('SequenceT', bound=Sequence)
HashableT = TypeVar('HashableT', bound=Hashable)


def merge_and_sort_into(seq_type, *iterables):
    # type: (Type[SequenceT], *Iterable[HashableT]) -> SequenceT[HashableT]
    return seq_type(sorted(set(chain(*iterables))))


def merge_and_sort_into_list(*iterables):
    # type: (*Iterable[HashableT]) -> List[HashableT]
    return merge_and_sort_into(list, *iterables)


def merge_and_sort_into_tuple(*iterables):
    # type: (*Iterable[HashableT]) -> Tuple[HashableT]
    return merge_and_sort_into(tuple, *iterables)

Код в порядке, но mypy не нравится тип возврата merge_and_sort_into(), говоря error: Type variable "SequenceT" used with arguments. Как я могу передать тип Sequence в функцию, а также использовать этот тип в качестве возвращаемого типа? (примечание: я не фиксирую тот факт, что тип значения для последовательности также должен быть сопоставимым / сортируемым, но давайте просто проигнорируем это).

Вот версия, которую mypy примет, но она нене захватывает ограничение, которое тип, переданный в merge_and_sort_into(), является его типом возврата, следовательно бросает.

def merge_and_sort_into(seq_type, *iterables):
    # type: (Callable[[Iterable[HashableT]], Sequence[HashableT]], *Iterable[HashableT]) -> Sequence[HashableT]
    return seq_type(sorted(set(chain(*iterables))))


def merge_and_sort_into_list(*iterables):
    # type: (*Iterable[HashableT]) -> List[HashableT]
    return cast(List[HashableT], merge_and_sort_into(list, *iterables))


def merge_and_sort_into_tuple(*iterables):
    # type: (*Iterable[HashableT]) -> Tuple[HashableT]
    return cast(Tuple[HashableT], merge_and_sort_into(tuple, *iterables))

1 Ответ

1 голос
/ 09 ноября 2019

Mypy не поддерживает переменные универсального типа, потому что они требуют, чтобы система типов поддерживала более высокие виды, как обсуждено в этом комментарии .

Даже если Mypy поддерживает переменные универсального типа, типподпись исходной функции будет неправильной, потому что не все Sequence s могут быть построены из итерируемого. Например,

class EmptyList(Sequence[T]):
  def __init__(self): pass
  def __getitem__(self, item): raise IndexError
  def __len__(self): return 0

EmptyList([1, 2, 3]) # TypeError

Наиболее простое решение для вашего конкретного случая, вероятно, состоит в том, чтобы просто разрешить непоследовательные типы возврата и использовать Callable вместо Type.

T, R = TypeVar('T'), TypeVar('R')

def chain_into(into: Callable[[Iterable[T]], R], *iters: Iterable[T]) -> R:
    return into(chain.from_iterable(iters))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...