mypy 0.6.4 返回类型Optional[str] 但有时你对你将得到的类型有先验知识

Ces*_*esc 9 python-3.x mypy

我有一个函数返回一个class instanceNone依赖于某些逻辑。\n在代码的某些地方我知道这个函数肯定不会返回None,\n但 mypy 抱怨。

\n\n

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

\n\n

我想避免标记a_string为,我\n知道我也可以使用或a_string: Optional[str] = ""克服问题,但不知何故我\n觉得可能有更好的方法。casttype ignore

\n\n

有什么建议如何处理这种情况?

\n\n

对于这个例子,我使用mypy 0.641python 3.7

\n\n
"""\nFunction returns either an object or none\n\n"""\n\nfrom typing import Optional, cast\n\nRET_NONE = False\n\n\ndef minimal_example() -> Optional[str]:\n    if RET_NONE:\n        return None\n    else:\n        return "my string"\n\n\na_string = ""\nmaybe_string = minimal_example()\na_string = maybe_string\n\n# mypy doesn\'t complain if I do the following\na_string = cast(str, maybe_string)\na_string = maybe_string  # type: ignore\n
Run Code Online (Sandbox Code Playgroud)\n\n

Mypy 抱怨如下:

\n\n
\xe2\x9d\xaf\xe2\x9d\xaf\xe2\x9d\xaf   mypy mypy_none_or_object.py                                                                                                                                                                         (chatsalot)  \xe2\x9c\x98 1\nmypy_none_or_object.py:19: error: Incompatible types in assignment (expression has type "Optional[str]", variable has type "str")\n
Run Code Online (Sandbox Code Playgroud)\n

Mic*_*x2a 14

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
Run Code Online (Sandbox Code Playgroud)

(如果您不知道,mypy 会对该函数进行特殊处理reveal_type(...):每当 mypy 遇到它时,mypy 都会打印出您提供的任何表达式的类型。这对于调试很有用,但是您应该记住在使用后删除伪函数已完成,因为它在运行时不存在。)

或者,您可以重新设计代码,以便函数的返回值更加规范化——它始终返回一个字符串,而不是有时返回一个字符串。

如果RET_NONEis 是一个或多或少不可变的全局变量(例如“启用调试模式”或“假设我们在 Windows 上运行”),您可以使用 is 来利用 mypy 和--always-trueflags--always-false并提供两个不同的定义的minimal_example。例如:

RET_NONE = False

if RET_NONE:
    def minimal_example() -> None:
        return None
else:
    def minimal_example() -> str:
        return str
Run Code Online (Sandbox Code Playgroud)

mypy --always-true RET_NONE然后,您可以使用或 来调用 mypymypy --always-false RET_NONE以匹配变量的定义方式。您可以在此处此处找到有关这些类型的更多信息。

您可以探索的第四种选择是使用函数重载:https://mypy.readthedocs.io/en/latest/more_types.html#function-overloading

但是,我不知道这是否真的适用于您的情况:您无法定义仅返回类型不同的重载:每个重载的参数数量或类型需要以某种方式彼此区分。


paw*_*cki 6

两种解决方案: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
Run Code Online (Sandbox Code Playgroud)

现在 mypy 确信下一行maybe_string绝对不会是None. 我在有关类型的博客文章的约束类型部分中介绍了这一点。