【问题标题】:Mypy behavior when the object passes through a typeshed function?对象通过类型函数时的Mypy行为?
【发布时间】:2019-11-29 23:40:34
【问题描述】:

我似乎不理解 Mypy 在以下情况下的行为。本题简化代码

import typing as t
...

self._store:t.Dict[str,str] = dict()
...

def set_kv(self, key:str, value:int)->t.Any:
    assert isinstance(key, six.string_types)
    assert isinstance(value, six.string_types)
    with self.__lock.write():
        self.__store[key] = value
        self.__persist()

我通过运行以下命令使用 mypy 测试此代码

mypy docido_sdk/index/test.py --ignore-missing-imports --follow-imports=error --strict-optional

现在理想情况下,这应该在self.__store[key]= value 行引发错误。但事实并非如此。 当我删除assert isinstance(value, six.string_types) 时,它才会抛出错误。 isinstance 是下面给出的典型函数

def isinstance(__o: object, __t: Union[type, Tuple[Union[type, Tuple], ...]]) -> bool: ...

这是mypy的一个bug还是预期的行为,因为如果我理解正确isinstance应该不会影响mypy对value类型的理解。

【问题讨论】:

  • 感谢guyz 的回复,我以为mypy 不知道six.string_types 是什么意思。两个答案都是正确的。

标签: python-3.x mypy


【解决方案1】:

当您调用isinstance() 时,您正在对value 的类型进行运行时断言。

在调用isinstance() 之前,mypy 认为valueint,但实际上在运行时它可能会有所不同。在isinstance() 之后调用value 必须是str

mypy 知道isinstance(),因此它把它作为一个明确的指令来覆盖它认为它知道的关于value 的任何内容,并相应地更新它的模型。

同样,如果您有value: Union[int, str],那么在检查if isinstance(value, str): ... 之后,mypy 可以更新其宇宙视图并知道value 现在是str 而不是int。在这种情况下,这似乎更直观。

这里的问题是可能的类型不相交,所以 mypy 只是将类型作为唯一可以是的类型:str

顺便说一句,typescript 可以更好地处理这种情况,因为它有一个 never 类型,用于处理这种永远不会发生的情况,并且当您意外地对 never 值进行操作时会产生错误。

Mypy 没有这个概念,所以它不能很好地捕捉到这类问题,因为它实际上可能是故意的:类型系统仅提供提示,因此您可能有合理的代码来生成运行时断言来处理案例类型假设错误的地方。

【讨论】:

  • 两个答案都是正确的,我只选择这个答案是因为它有更多相关的细节。我也可能最终会在我的研究中使用一些 cmets ;D.
【解决方案2】:

这似乎是 mypy 的预期行为:mypy 确实使用 isinstance 调用进行类型推断,并且 mypy 不会在无法访问的代码中引发类型错误。

根据current documentation,mypy 使用isinstance 检查来推断变量的类型(尽管不是像type(o) is ... 这样的表达式)。它给出了以下示例:

def f(o: object) -> None:
    if isinstance(o, int):  # Mypy understands isinstance checks
        g(o + 1)        # Okay; type of o is inferred as int here
        ...

其中显示仅当oint 时才可访问的代码。 Mypy 意识到这一点并假设oint。这是合理的,因为如果它不是真的,代码是不可能运行的。

在您的代码中,self.__store[key] = value 仅在 valuestr 时才可访问(在 Python 3 下,six.string_types(str,))。您的代码的不同之处在于断言不可能为真。所以代码根本不会运行。因此,该代码无法运行并导致类型错误。

Elsewhere in the documentation,他们举了一个 mypy 不类型检查无法访问代码的例子:

from typing import NoReturn

def stop() -> NoReturn:
    raise Exception('no way')

Mypy 将确保注释为返回 NoReturn 的函数真正永远不会返回,无论是隐式还是显式。 Mypy 还会识别出调用此类函数后的代码无法访问,并会做出相应的行为:

def f(x: int) -> int:
    if x == 0:
        return x
    stop()
    return 'whatever works'  # No error in an unreachable block

注意他们使用的措辞:“将相应地行事”。这就是许多人期望类型检查器对无法访问的代码所做的事情。

【讨论】:

  • 感谢回复,我以为mypy不知道six.string_types是什么意思
猜你喜欢
  • 1970-01-01
  • 2021-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-16
  • 1970-01-01
相关资源
最近更新 更多