对象通过类型函数时的Mypy行为?

时间:2019-11-29 15:06:51

标签: python-3.x mypy

在以下情况下,我似乎不了解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的错误或预期行为,因为如果我正确理解isinstance不会影响对value类型的mypy的理解。

2 个答案:

答案 0 :(得分:2)

调用isinstance()时,您将在运行时声明value类型。

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

mypy知道isinstance(),因此将其视为显式指令来覆盖其对value的了解并相应地更新其模型。

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

这里的问题是,可能的类型不相交,因此mypy仅将类型作为唯一的类型:int

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

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

答案 1 :(得分:1)

这似乎是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] = valuevalue时才能访问str(在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

请注意他们使用的措辞:“将相应地表现”。这就是许多人希望类型检查器对无法访问的代码执行的操作。