Как проверить тип использования next() с результатом groupby, используя mypy?

Я пытаюсь использовать mypy для некоторых функций utils в своем проекте, но у меня возникают проблемы с этой функцией, которая объединяет groupby и next.

Это код функции:

from itertools import groupby
from typing import Iterable, Any


def all_same(iterable: Iterable[Any]) -> bool:
    """Return True if all elements in iterable are equal
    >>> all_same([3, 3, 3])
    True
    >>> all_same([3, 3, 1])
    False
    >>> all_same([])
    True
    >>> all_same(['a', 'a'])
    True
    """
    g = groupby(iterable)
    return bool(next(g, True)) and not bool(next(g, False))

Я продолжаю получать эту ошибку о невозможности вывести type argument 1 of "next":

$ mypy testing.py 
testing.py: note: In function "all_same":
testing.py:17: error: Cannot infer type argument 1 of "next"

Я полагаю, это означает, что он не может вывести здесь тип g, верно?

Мне трудно понять, проблема ли это в аннотациях моего типа или в аннотациях типа для groupby.

Для справки, это аннотация типа для groupby:

@overload
def groupby(iterable: Iterable[_T]) -> Iterator[Tuple[_T, Iterator[_T]]]: ...

Таким образом, это означает, что «groupby принимает итерацию типа T и возвращает итератор кортежей, содержащих два элемента: (один элемент типа T, итератор объектов типа T)». Выглядит хорошо для меня, но тогда mypy должен быть в состоянии вывести первый аргумент next как Iterator[Tuple[Any, Iterator[Any]]], верно?

Что мне не хватает?


person Elias Dorneles    schedule 29.06.2016    source источник


Ответы (1)


Причина связана с аннотацией типа для next. Функция next определена как имеющая следующую сигнатуру типа:

@overload
def next(i: Iterator[_T]) -> _T: ...
@overload
def next(i: Iterator[_T], default: _T) -> _T: ...

По сути, mypy ожидает, что тип значения по умолчанию будет таким же, как содержимое того, что у вас есть в итераторе.

Однако g будет иметь тип Iterator[Tuple[Any, Iterator[Any]]], а Tuple[Any, Iterator[Any]] не будет того же типа, что и bool.

К сожалению, я не уверен, каким будет лучший способ восстановить ваш алгоритм для проверки типов, поскольку данная сигнатура типа для next кажется мне довольно разумной + кажется маловероятной, что она будет изменена (хотя вы можете зарегистрировать проблему, если хотите аргументировать в пользу этого изменения?). Возможно, ответы здесь могут оказаться полезными?

person Michael0x2a    schedule 29.06.2016
comment
Спасибо! Хотя нет смысла менять мой алгоритм из-за постепенного набора текста, алгоритм верный. - person Elias Dorneles; 29.06.2016
comment
Это все еще странно для меня, потому что ошибка связана с невозможностью определить тип. - person Elias Dorneles; 29.06.2016
comment
@elias - Ну, дело в том, что, согласно mypy, ваш алгоритм ненадежен - ваш код, к лучшему или к худшему, не учитывает сигнатуру типа next. Причина, по которой mypy жалуется на то, что не может определить тип, в основном связана с этой проблемой - он пытается согласовать несоответствие и терпит неудачу с этим конкретным сообщением об ошибке. Но да, тот факт, что mypy сообщает об этой конкретной ошибке вместо ошибки «ваши типы не совпадают», скорее всего, является ошибкой. - person Michael0x2a; 29.06.2016
comment
ну, я отказываюсь менять алгоритм из-за аннотации типа next, ха-ха! Я получил его от Рэймонда Хеттингера (основной разработчик Python), и он довольно хорош. :) Спасибо, что докопались до конца! - person Elias Dorneles; 30.06.2016