mypy «Необязательно [Dict [Any, Any]]» не индексируется внутри стандартного фильтра, карта - PullRequest
0 голосов
/ 22 апреля 2020

Учитывая следующий код:

from typing import Optional, Dict

def foo(b: bool) -> Optional[Dict]:
    return {} if b else None


def bar() -> None:
    d = foo(False)

    if not d:
        return

    filter(lambda x: d['k'], [])

mypy 0.770 завершается ошибкой со следующей ошибкой в ​​последней строке bar: Value of type "Optional[Dict[Any, Any]]" is not indexable. То же самое касается map. Изменение строки для использования понимания списка или filter_ или map_ из pyda sh устраняет ошибку.

Почему mypy выдает ошибку при использовании стандарта filter, даже если есть защита типа

Ответы [ 2 ]

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

mypy не имитирует код: он не знает, что d не равно None, если вызов на filter действительно достигнут. Он только знает, что есть попытка проиндексировать что-то, что было статически помечено как возможно имеющее None в качестве значения. (Другими словами, тип c stati d не изменится, если вы фактически не назначите значение с другим типом c stati.)

Вы можете помочь mypy, используя cast функция.

from typing import Optional, Dict<b>, cast</b>

def foo(b: bool) -> Optional[Dict]:
    return {} if b else None


def bar() -> None:
    d = foo(False)

    if not d:
        return

    <b>d: dict = cast(dict, d)  # "Trust me, mypy: d is a dict"</b>

    filter(lambda x: d['k'], [])
0 голосов
/ 22 апреля 2020

Сужение типов, которое происходит после if или assert, не распространяется на внутренние области, в которых вы связали эту переменную. Простой обходной путь - определить новую переменную, связанную с более узким типом, Например:

def bar() -> None:
    d = foo(False)

    if not d:
        return
    d_exists = d

    filter(lambda x: d_exists['k'], [])

Причина, по которой d не привязан к более узкому типу во внутренней области видимости, может заключаться в том, что нет гарантии того, что d может не измениться на None в внешняя область действия, например:

def bar() -> None:
    d = foo(False)

    if not d:
        return

    def f(x: str) -> str:
        assert d is not None  # this is totally safe, right?
        return d['k']         # mypy passes because of the assert

    d = None  # oh no!
    filter(f, [])

, тогда как если вы связываете новую переменную, это присвоение невозможно:

def bar() -> None:
    d = foo(False)

    if not d:
        return
    d_exists = d

    def f(x: str) -> str:
        # no assert needed because d_exists is not Optional
        return d_exists['k']

    d_exists = None  # error: Incompatible types in assignment
    filter(f, [])

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

...