使用裸变量和 TypedDicts 的不同 Mypy 行为

问题描述 投票:0回答:1

我仍然在学习 Python,并且很早就开始使用 Mypy,以避免忘记使用正确的类型并检查代码中缺少的内容。到目前为止,大多数事情都进展顺利,我对检查器从代码中读取的内容感到惊讶,并且没有抱怨。

今天我发现了一些让我困惑的情况。使用以下非常简化的示例代码

from typing import Optional

myvar: Optional[int] = None

myvar = 42

if myvar < 1:
    print("Error")

Mypy 没有什么可抱怨的,因为它显然知道

myvar
不能是
None
语句中的
if

如果我使用

TypedDict
,那就很奇怪了,这在下面的片段中并不成立:

from typing import Optional, TypedDict

class MyDictType(TypedDict):
    mykey: Optional[int]

mydict1: MyDictType = {'mykey': None}

mydict1['mykey'] = 42

if mydict1['mykey'] < 1:
    print("Error")

Mypy 在这里抱怨

Unsupported operand types for > ("int" and "None")
Left operand is of type "Optional[int]"
。它“迫使”我直接检查喜欢

if mydict1['mykey'] is not None and mydict1['mykey'] < 1:
    print("Error")

因此我的问题是:为什么 Mypy 不能识别与

mydict1['mykey']
完全相同的东西(
None
不可能是
TypedDict
)?

python mypy python-typing
1个回答
0
投票

您的两个示例并不相同,因此由于 Python 和 Mypy 中的赋值规则而导致不同的行为。首先,您将

myvar
替换为全新的作业
42
,其类型为
int
,而不是
Optional[int]
。第二个示例不会导致对
mydict1
进行全新的分配(因为类型检查最终是针对基础名称的分配进行的 - 将新值分配给键
'mykey'
只会影响
mydict1
内的不透明内部值,无需对
mydict1
的分配进行实质性更改。但这仅回答了部分问题,因为 Mypy 会正确标记类型不匹配问题,您可以将
'forty-two'
分配给
myvar
,那么 Mypy 真正看到了什么?

为了找出答案,让我们使用 Mypy 中的

reveal_type
助手。将其添加到受影响的分配之后的行中:

from typing import Optional

myvar: Optional[int] = None
reveal_type(myvar)

myvar = 42
reveal_type(myvar)

if myvar < 1:
    print("Error")

通过 Mypy 运行上述内容

so78038503_1.py:4: note: Revealed type is "Union[builtins.int, None]"
so78038503_1.py:7: note: Revealed type is "builtins.int"
Success: no issues found in 1 source file

注意 Mypy 只是假设

myvar
在这种情况下变成了
int
,因此不会将简单比较标记为错误(尽管底层类型注释没有改变一点,这可以通过检查
 来验证) locals()['__annotations__']
)。

对于第二个例子,做同样的

reveal_type
检查:

from typing import Optional, TypedDict

class MyDictType(TypedDict):
    mykey: Optional[int]

mydict1: MyDictType = {'mykey': None}
reveal_type(mydict1['mykey'])

mydict1['mykey'] = 42
reveal_type(mydict1['mykey'])

if mydict1['mykey'] < 1:
    print("Error")

检查会注意到

mydict1['mykey']
保持类型
Union[builtins.int, None]
而不会下降到
builtins.int

so78038503_2.py:7: note: Revealed type is "Union[builtins.int, None]"
so78038503_2.py:10: note: Revealed type is "Union[builtins.int, None]"
so78038503_2.py:12: error: Unsupported operand types for > ("int" and "None")  [operator]
so78038503_2.py:12: note: Left operand is of type "int | None"
Found 1 error in 1 file (checked 1 source file)

请注意,

pyright
对于第二个示例不会有任何问题,因为它假设两种情况下的最终值都是
Literal[42]
,即
builtins.int
,尽管我个人认为这种行为可能会导致脆性,因为正如您所指出的,通过实际的
Optional[int]
进行作业中的微妙变化以揭示缺乏对
None
的防范,这破坏了类型检查的全部意义。

© www.soinside.com 2019 - 2024. All rights reserved.