mypy 0.6.4 тип возвращаемого значения Необязательно [str], но иногда у вас есть предварительные знания о типе, который вы получите - PullRequest
0 голосов
/ 13 ноября 2018

У меня есть функция, которая возвращает либо class instance, либо None в зависимости от некоторой логики.В некоторых местах кода я знаю, что эта функция наверняка не возвращает None, но mypy жалуется.

Я сделал минимальный пример, который воспроизводит ситуацию, описанную выше.

Я бы не хотел помечать a_string как a_string: Optional[str] = "", я знаю, что могу также решить эту проблему, используя cast или type ignore, но я чувствую, что может быть что-то лучше.

Любые рекомендации, как справиться с этой ситуацией?

В этом примере я использую mypy 0.641 и python 3.7

"""
Function returns either an object or none

"""

from typing import Optional, cast

RET_NONE = False


def minimal_example() -> Optional[str]:
    if RET_NONE:
        return None
    else:
        return "my string"


a_string = ""
maybe_string = minimal_example()
a_string = maybe_string

# mypy doesn't complain if I do the following
a_string = cast(str, maybe_string)
a_string = maybe_string  # type: ignore

Mypy жалуется следующим образом:

❯❯❯   mypy mypy_none_or_object.py                                                                                                                                                                         (chatsalot)  ✘ 1
mypy_none_or_object.py:19: error: Incompatible types in assignment (expression has type "Optional[str]", variable has type "str")

Ответы [ 2 ]

0 голосов
/ 13 ноября 2018

Mypy предназначена для обработки сигнатур функций как «источника правды».Если вы укажете, что какая-то функция возвращает Optional[str], тогда mypy будет считать, что так будет всегда.Он не будет пытаться увидеть, как какие-либо глобальные переменные могут или не могут изменить сигнатуру этой функции.

Самый простой способ обойти это - добавить проверку assert или isinstance:

maybe_string = minimal_example()
reveal_type(maybe_string)           # Revealed type is Optional[str]
assert maybe_string is not None     # Or use 'if isinstance(maybe_string, str)
reveal_type(maybe_string)           # Revealed type is str

(Если вы не знаете, mypy будет в особом случае использовать функцию reveal_type(...): всякий раз, когда mypy встречает ее, mypy выводит тип любого выражения, которое вы предоставляете. Это полезно для отладки, но вы должныне забудьте удалить псевдо-функцию после того, как вы закончите, так как она не существует во время выполнения.)

В качестве альтернативы, вы можете изменить код, чтобы возвращаемое значение вашей функции было более нормализованным - оно всегда возвращаетстрока вместо того, чтобы иногда возвращать единицу.

Если RET_NONE означает более или менее неизменный глобал (например, что-то вроде «включить режим отладки» или «предположим, что мы работаем в Windows»),Вы можете использовать это, чтобы воспользоваться флагами --always-true и --always-false mypy и предоставить два разных определения minimal_example.Например:

RET_NONE = False

if RET_NONE:
    def minimal_example() -> None:
        return None
else:
    def minimal_example() -> str:
        return str

Затем вы вызываете mypy, используя mypy --always-true RET_NONE или mypy --always-false RET_NONE, чтобы соответствовать определению вашей переменной.Вы можете найти больше информации об этих типах здесь и, возможно, здесь .

Четвертая альтернатива, которую вы можете изучить, - это использование перегрузок функций: https://mypy.readthedocs.io/en/latest/more_types.html#function-overloading

Однако, idk, если это действительно работает в вашем случае: вы не можете определить перегрузки, когда отличается только тип возвращаемого значения: аргументарность или типы каждой перегрузки должны каким-то образом отличаться друг от друга.

0 голосов
/ 13 ноября 2018

Оба решения: cast() и # type: ignore эффективно отключают проверку mypy для переменной. Это может скрывать ошибки, и по возможности их следует избегать.

В вашем случае mypy не может знать значение RET_NONE, так как оно может быть изменено во время выполнения с False на что-либо еще, таким образом, ошибка.

Я предлагаю добавить утверждение:

a_string = ""
maybe_string = minimal_example()
assert maybe_string is not None   # <- here
a_string = maybe_string

Теперь mypy уверен, что на следующей строке maybe_string определенно не будет None. Я рассмотрел это в Ограничивающих типах разделе моего блога о печати. ​​

...