ошибка mypy-несовместимый тип, несмотря на использование 'Union'
Рассмотрим следующий пример кода:
from typing import Dict, Union
def count_chars(string) -> Dict[str, Union[str, bool, int]]:
result = {} # type: Dict[str, Union[str, bool, int]]
if isinstance(string, str) is False:
result["success"] = False
result["message"] = "Inavlid argument"
else:
result["success"] = True
result["result"] = len(string)
return result
def get_square(integer: int) -> int:
return integer * integer
def validate_str(string: str) -> bool:
check_count = count_chars(string)
if check_count["success"] is False:
print(check_count["message"])
return False
str_len_square = get_square(check_count["result"])
return bool(str_len_square > 42)
result = validate_str("Lorem ipsum")
При запуске mypy с этим кодом возвращается следующая ошибка:
error: Argument 1 to "get_square" has incompatible type "Union[str, bool, int]"; expected "int"
И я не уверен, как я мог бы избежать этой ошибки без использования Dict[str, Any]
в качестве возвращаемого типа в первой функции или установки расширения 'TypedDict' mypy. Является ли mypy на самом деле "правильным", любой мой код не является типобезопасным или это должно рассматриваться как ошибка mypy?
1 ответ:
Mypy здесь прав - если значения в вашем dict могут быть strs, ints или bools, то строго говоря, мы не можем предположить, что
У вас есть несколько способов решить эту проблему. Первый способ-это на самом деле простопроверить Типcheck_count["result"]
всегда будет вычисляться точно int.check_count["result"]
, чтобы увидеть, является ли это int. Это можно сделать с помощью assert:assert isinstance(check_count["result"], int) str_len_square = get_square(check_count["result"])
...или, возможно, утверждение if:
if isinstance(check_count["result"], int): str_len_square = get_square(check_count["result"]) else: # Throw some kind of exception here?
Mypy понимает проверки типа этой формы в утверждениях и утверждениях if (к a ограниченный объем).
Однако, это может стать утомительным разбрасыванием этих проверок по всему вашему коду. Так что, возможно, лучше всего просто отказаться от использования диктов и переключиться на использование классов.
То есть определите класс:
class Result: def __init__(self, success: bool, message: str) -> None: self.success = success self.message = message
...и верните экземпляр этого вместо этого.
Это несколько более неудобно в том смысле, что если ваша цель-в конечном счете вернуть / манипулировать json, вам теперь нужно написать код для преобразования этого класса из/в json, но это позволит вам избежать ошибки, связанные с типом.
Определение пользовательского класса может быть немного утомительным, поэтому вместо него можно попробовать использовать тип NamedTuple:
from typing import NamedTuple Result = NamedTuple('Result', [('success', bool), ('message', str)]) # Use Result as a regular class
Вам все еще нужно написать код tuple -> json, и IIRC namedtuples (как обычная версия из модуля
collections
, так и этот типизированный вариант) менее эффективны, чем классы, но, возможно, это не имеет значения для вашего варианта использования.