对象通过类型化函数时的 Mypy 行为?
Mypy behavior when the object passes through a typeshed function?
我似乎不理解 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 是下面给出的 typshed 函数
def isinstance(__o: object, __t: Union[type, Tuple[Union[type, Tuple], ...]]) -> bool: ...
这是 mypy 的错误还是预期行为,因为如果我理解正确 isinstance
应该不会影响 mypy 对 value
类型的理解。
当您调用 isinstance()
时,您正在对 value
的类型进行运行时断言。
在调用 isinstance()
之前,mypy 认为 value
是一个 int
,但实际上在运行时它可能有所不同。 isinstance()
调用后value
一定是str
.
mypy 知道 isinstance()
,因此它将它作为一条显式指令来覆盖它认为它知道的关于 value
的任何内容并相应地更新其模型。
同样,如果您有 value: Union[int, str]
,那么在检查 if isinstance(value, str): ...
之后,mypy 可以更新它对宇宙的看法,并且知道 value
现在是 str
而不是 int
。在这种情况下,这看起来更直观。
这里的问题是可能的类型不相交,所以 mypy 只是将类型作为唯一可能的类型:str
.
顺便说一句,打字稿会更好地处理这种情况,因为它有一个类型 never
用于这种永远不会发生的情况,并且当您对 never
值进行操作时会产生错误没想到
Mypy 没有这个概念,所以它不太擅长捕捉这类问题,因为它实际上可能是故意的:类型系统仅提供提示,因此您可能合理地拥有使运行时断言的代码处理类型假设错误的情况。
这似乎是 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
...
显示只有 o
是 int
时才能访问的代码。 Mypy 意识到这一点并假设 o
是一个 int
。这是合理的,因为代码不可能 运行 如果它不是真的。
在您的代码中,只有当 value
是 str
时才能访问 self.__store[key] = value
(在 Python 3 下,six.string_types
是 (str,)
).您的代码的不同之处在于断言不可能为真。所以代码根本就不会 运行s。因此,该代码无法成为 运行 并导致类型错误。
Elsewhere in the documentation,他们给出了一个mypy没有类型检查不可达代码的例子:
from typing import NoReturn
def stop() -> NoReturn:
raise Exception('no way')
Mypy will ensure that functions annotated as returning NoReturn
truly never return, either implicitly or explicitly. Mypy will also recognize that the code after calls to such functions is unreachable and will behave accordingly:
def f(x: int) -> int:
if x == 0:
return x
stop()
return 'whatever works' # No error in an unreachable block
注意他们使用的措辞:"will behave accordingly"。这是许多人期望类型检查器对无法访问的代码执行的操作。
我似乎不理解 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 是下面给出的 typshed 函数
def isinstance(__o: object, __t: Union[type, Tuple[Union[type, Tuple], ...]]) -> bool: ...
这是 mypy 的错误还是预期行为,因为如果我理解正确 isinstance
应该不会影响 mypy 对 value
类型的理解。
当您调用 isinstance()
时,您正在对 value
的类型进行运行时断言。
在调用 isinstance()
之前,mypy 认为 value
是一个 int
,但实际上在运行时它可能有所不同。 isinstance()
调用后value
一定是str
.
mypy 知道 isinstance()
,因此它将它作为一条显式指令来覆盖它认为它知道的关于 value
的任何内容并相应地更新其模型。
同样,如果您有 value: Union[int, str]
,那么在检查 if isinstance(value, str): ...
之后,mypy 可以更新它对宇宙的看法,并且知道 value
现在是 str
而不是 int
。在这种情况下,这看起来更直观。
这里的问题是可能的类型不相交,所以 mypy 只是将类型作为唯一可能的类型:str
.
顺便说一句,打字稿会更好地处理这种情况,因为它有一个类型 never
用于这种永远不会发生的情况,并且当您对 never
值进行操作时会产生错误没想到
Mypy 没有这个概念,所以它不太擅长捕捉这类问题,因为它实际上可能是故意的:类型系统仅提供提示,因此您可能合理地拥有使运行时断言的代码处理类型假设错误的情况。
这似乎是 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
...
显示只有 o
是 int
时才能访问的代码。 Mypy 意识到这一点并假设 o
是一个 int
。这是合理的,因为代码不可能 运行 如果它不是真的。
在您的代码中,只有当 value
是 str
时才能访问 self.__store[key] = value
(在 Python 3 下,six.string_types
是 (str,)
).您的代码的不同之处在于断言不可能为真。所以代码根本就不会 运行s。因此,该代码无法成为 运行 并导致类型错误。
Elsewhere in the documentation,他们给出了一个mypy没有类型检查不可达代码的例子:
from typing import NoReturn def stop() -> NoReturn: raise Exception('no way')
Mypy will ensure that functions annotated as returning
NoReturn
truly never return, either implicitly or explicitly. Mypy will also recognize that the code after calls to such functions is unreachable and will behave accordingly:def f(x: int) -> int: if x == 0: return x stop() return 'whatever works' # No error in an unreachable block
注意他们使用的措辞:"will behave accordingly"。这是许多人期望类型检查器对无法访问的代码执行的操作。