我有一个函数会返回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")
答案 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将假定情况始终如此。它不会尝试查看任何全局变量如何改变或不改变该函数签名。
解决此问题的最简单方法是添加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上运行”之类的东西),则可以利用来利用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_NONE
或mypy --always-false RET_NONE
调用mypy以匹配变量的定义方式。您可以找到有关这些类型here,也许还有here的更多信息。
您可以探索的第四个替代方法是使用函数重载:https://mypy.readthedocs.io/en/latest/more_types.html#function-overloading
但是,idk是否确实适用于您的情况:您不能在仅返回类型不同的情况下定义重载:每个重载的参数arity或类型都需要以某种方式彼此区分。