【问题标题】:typing/mypy: difference between dict[key] and dict.get(key)?打字/mypy: dict[key] 和 dict.get(key) 的区别?
【发布时间】:2021-06-24 23:53:12
【问题描述】:

这是我的测试脚本:

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 行的呼叫?

【问题讨论】:

标签: python mypy python-typing


【解决方案1】:

只是一个假设:

dict.__getitem__() (a.k.a 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。

【讨论】:

  • 但是 18 和 19 都会引发错误; 22 的不同之处仅在于被索引的对象是实例属性,而不是其值静态已知的对象。
  • 嗯,你是对的。我想这就是 mypy 的局限性。它查看config: dict 并且根本不知道config[...] 的类型(因为它没有在dict.__getattr__ 的类型注释中定义)。它看到dict.get 有类型注释-> Optional[Any]
  • 即使mypy --strict 似乎也不在乎这种不匹配。要么是mypy 中的错误,要么是我不理解的未注释函数的处理方式有一些微妙之处。 (我假设test_func 需要int 会覆盖__getitem__ 缺少定义的返回值。)
  • test_func(foo()) 出现同样的错误,其中foo 未注释,所以我认为我最初认为a.config 是一个实例属性并不相关。
  • @chepner 更新了我的答案
猜你喜欢
  • 2012-06-17
  • 2016-01-20
  • 1970-01-01
  • 2016-08-02
  • 1970-01-01
  • 2011-04-20
  • 1970-01-01
相关资源
最近更新 更多