输入/mypy:dict[key] 和 dict.get(key) 之间的区别?

问题描述

这是我的测试脚本:

class A:
    def __init__(self,config: dict) -> None:
        self.config = config


def test_func(a: int) -> None:
    pass


main_config = {"a": "x"}
a = A(config=main_config)

# value direct
test_func(1)  # line 14
test_func("b")  # line 15

# value with wrong type from dict
test_func(main_config["a"])  # line 18
test_func(main_config.get("a"))  # line 19

# value with wrong type from dict in class instance
test_func(a.config["a"])  # line 22
test_func(a.config.get("a"))  # line 23

如果我用 mypy (0.910) 测试它,我会得到以下结果:

> mypy test.py                                                                                                                                                                                                                                           
test.py:15: error: Argument 1 to "test_func" has incompatible type "str"; expected "int"
tests.py:18: error: Argument 1 to "test_func" has incompatible type "str"; expected "int"
tests.py:19: error: Argument 1 to "test_func" has incompatible type "Optional[str]"; expected "int"
tests.py:23: error: Argument 1 to "test_func" has incompatible type "Optional[Any]"; expected "int"
Found 4 errors in 1 file (checked 1 source file)


为什么 mypy 错过/不报告第 22 行的电话?

解决方法

只是一个假设:

dict.__getitem__()(又名 a.config[...])没有类型注释。虽然 dict.get 有:

def get(self,key: _KT) -> Optional[_VT_co]: ...

我的假设的简单证明:

from typing import Optional,Any


def test_func(a: int) -> None:
    pass

def foo(a):
    if a == 1:
        return 123
    elif a == 2:
        return 'asd'
    else:
        raise ValueError


def foo_typed(a) -> Optional[Any]:
    if a == 1:
        return 123
    elif a == 2:
        return 'asd'
    else:
        return None


test_func(foo(2))
test_func(foo_typed(2)) # line 26

仅生产:

main.py:26: error: Argument 1 to "test_func" has incompatible type "Optional[Any]"; expected "int"

即使 foo(2) 返回 string。