Почему mypy не выводит аннотацию функции из @overload? - PullRequest
0 голосов
/ 04 апреля 2020

Я запускаю mypy с опцией disallow-untyped-defs. Когда я аннотирую тип функции с помощью @overload и опускаю аннотацию в определении, mypy по-прежнему выдает ошибку. Мне кажется, что эту функцию следует считать аннотированной.

example.py:

from typing import overload
@overload
def f(arg: int) -> int: ...
@overload
def f(arg: str) -> str: ...

def f(arg):
    if type(arg) == int:
        return 1
    elif type(arg) == str:
        return "a"
    else:
        raise ValueError

Командная строка:

mypy --disallow-untyped-defs example.py

Вывод:

example.py:7: error: Function is missing a type annotation
Found 1 error in 1 file (checked 1 source file)

1 Ответ

0 голосов
/ 04 апреля 2020

Это предполагаемое поведение. Mypy хочет, чтобы вы добавили аннотации типов к реализации ваших перегрузок, например, так:

from typing import overload, Union

@overload
def f(arg: int) -> int: ...
@overload
def f(arg: str) -> str: ...
def f(arg: Union[int, str]) -> Union[int, str]:
    if type(arg) == int:
        return 1
    elif type(arg) == str:
        return "a"
    else:
        raise ValueError

Таким образом, mypy по-прежнему будет иметь всю информацию, необходимую для успешной проверки типов, для проверки тела f.

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

Например, вот более сложный пример перегрузки:

@overload
def zip(/, i1: Iterable[_T1]) -> Iterator[Tuple[_T1]]: ...
@overload
def zip(/, i1: Iterable[_T1], i2: Iterable[_T2]) -> Iterator[Tuple[_T1, _T2]]: ...
@overload
def zip(/, i1: Iterable[_T1], i2: Iterable[_T2],
        i3: Iterable[_T3]) -> Iterator[Tuple[_T1, _T2, _T3]]: ...
@overload
def zip(/, i1: Iterable[_T1], i2: Iterable[_T2], i3: Iterable[_T3],
        i4: Iterable[_T4]) -> Iterator[Tuple[_T1, _T2, _T3, _T4]]: ...
@overload
def zip(/, i1: Iterable[_T1], i2: Iterable[_T2], i3: Iterable[_T3],
        i4: Iterable[_T4], i5: Iterable[_T5]) -> Iterator[Tuple[_T1, _T2, _T3, _T4, _T5]]: ...
@overload
def zip(/, i1: Iterable[Any], i2: Iterable[Any], i3: Iterable[Any],
        i4: Iterable[Any], i5: Iterable[Any], i6: Iterable[Any],
        *remainder: Iterable[Any]) -> Iterator[Tuple[Any, ...]]: ...
def zip(*iterables: Iterable[Any]) -> Iterator[Tuple[Any, ...]]:
    sentinel = object()
    iterators = [iter(it) for it in iterables]
    while iterators:
        result = []
        for it in iterators:
            elem = next(it, sentinel)
            if elem is sentinel:
                return
            result.append(elem)
        yield tuple(result)

Для mypy было бы довольно сложно определить, какой именно должна быть сигнатура реализации в этом примере: число аргументов не совпадают, выяснить, что делать со всеми TypeVars (оставить их? отбросить их?) сложно ...

Это все проблемы, которые mypy может теоретически решить, но неясно, если это действительно даже экономит столько времени для пользователя в долгосрочной перспективе. В таких сложных случаях, как этот, пользователь, вероятно, предпочел бы указать, что именно является сигнатурой реализации.

(И если проблема является (а) сложной и (б) незначительной, она имеет тенденцию к go неразрешено, особенно в проектах с открытым исходным кодом.)

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

...