【问题标题】:mypy error - incompatible type despite using 'Union'mypy 错误 - 尽管使用了“Union”,但类型不兼容
【发布时间】:2017-10-14 09:24:01
【问题描述】:

考虑以下代码示例:

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 错误?

【问题讨论】:

    标签: python types type-hinting mypy typechecking


    【解决方案1】:

    Mypy 在这里是正确的——如果你的 dict 中的值可以是 strs、ints 或 bools,那么严格来说我们不能假设 check_count["result"] 总是会精确地计算为 int。

    您有几种方法可以解决此问题。第一种方法实际上只是检查 check_count["result"] 的类型以查看它是否为int。您可以使用断言来做到这一点:

    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 语句中理解这种形式的类型检查(在有限的范围内)。

    但是,将这些检查分散在整个代码中会很乏味。因此,实际上最好放弃使用 dicts 并转而使用类。

    即定义一个类:

    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 模块的常规版本和此类型的变体)的性能不如类,但也许这对您的使用无关紧要案子。

    【讨论】:

      猜你喜欢
      • 2022-08-13
      • 2017-10-10
      • 2022-01-08
      • 2022-06-14
      • 1970-01-01
      • 1970-01-01
      • 2022-01-19
      • 2021-12-21
      • 2021-06-02
      相关资源
      最近更新 更多