核心问题
MyPy 无法识别/验证返回值x[idx]
是否为布尔类型。
if isinstance(x[idx], bool) is True:
return x[idx]
完整的示例代码
from typing import Union, Optional
x: dict[str, Union[bool, str]] = {'a': True, 'b': 'foo'}
def return_if_bool(idx: str) -> Optional[bool]:
if isinstance(x[idx], bool):
return x[idx]
return None
print(return_if_bool('a'))
MyPy 返回
8: error: Incompatible return value type (got "bool | str", expected "bool | None") [return-value]
解决方法
有效的是使用typing.cast:
return typing.cast(bool, x[idx])
@Wombatz 指出了避免强制转换的“更干净” (imo)y = x[idx]
解决方法 - 分配如下:
y: Union[bool, str] = x[idx]
if isinstance(y, bool):
return y
为什么?
我希望这可以工作,而不必诉诸typing.cast()
或分配给一个变量y = x[idx]
,但无法完全弄清楚为什么它不能,特别是因为它看起来非常微不足道。
可能的
解释 @Wombatz 输入 MyPy 无法知道 x[idx] 将在重复访问时返回相同的值,这是有道理的,并且非常接近完全回答这个问题。然而,MyPy 似乎非常能够识别迭代器等所表现出的行为,因此它仍然有些奇怪。
您访问字典两次,但只检查结果一次。Mypy 无法知道重复访问相同的键将始终返回相同的类型(它不应该,因为 dict 子类可能会做一些奇怪的事情)。
简而言之:仅访问字典一次,检查该类型并返回值。
名称的缩小
value
是持久的,但任意表达式的缩小不是。这也适用于属性访问或其他函数调用。
因此,如果您想为 mypy 输入 check 某些内容,但它“忘记”了该检查,请始终尝试将表达式保存在变量中。
我相信这是重复的,但不可能在移动设备上找到
附录:如果 mypy 没有“忘记”实例检查,这里是一个违反类型规则的对象
当然,这是一个愚蠢的实现,但更一般的情况 - 子类可以为相同的方法调用返回联合的不同变体 - mypy 以相同的方式处理。
因此,回答您问题的这一部分: mypy 显然在访问 dict 时没有专门化类型缩小,即使可以证明 dict 不是子类。