mypy 0.6.4返回类型可选[str],但有时您对所获得的类型有先验知识

时间:2018-11-13 09:23:05

标签: python-3.x mypy

我有一个函数会返回class instanceNone,具体取决于 在某种逻辑上。 在代码的某些地方,我知道此函数肯定不会返回None, 但是mypy抱怨。

我做了一个最小的例子,重现了上述情况。

我想避免将a_string标记为a_string: Optional[str] = "",我 知道我也可以使用casttype ignore来解决问题,但是我不知何故 觉得可能会有更好的方法。

任何建议如何处理这种情况?

在此示例中,我使用mypy 0.641python 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 :(得分:2)

两种解决方案: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。我在博客文章的Constraining types部分中介绍了这一点。

答案 1 :(得分:2)

Mypy旨在将功能签名视为“真理之源”。如果您指示某个函数返回了Optional[str],则mypy将假定情况始终如此。它不会尝试查看任何全局变量如何改变或不改变该函数签名。

解决此问题的最简单方法是添加assertisinstance支票:

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上运行”之类的东西),则可以利用来利用mypy的--always-true--always-false标志,并提供minimal_example的两个不同定义。例如:

RET_NONE = False

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

然后,您使用mypy --always-true RET_NONEmypy --always-false RET_NONE调用mypy以匹配变量的定义方式。您可以找到有关这些类型here,也许还有here的更多信息。

您可以探索的第四个替代方法是使用函数重载:https://mypy.readthedocs.io/en/latest/more_types.html#function-overloading

但是,idk是否确实适用于您的情况:您不能在仅返回类型不同的情况下定义重载:每个重载的参数arity或类型都需要以某种方式彼此区分。