在今天的文章中,我们将介绍TypeGuard ,这是一种新的特殊类型,允许我们创建自定义的类型缩小函数。
TypeGuard 是在PEP 647中定义的,在Python 3.10以上版本中可用,或者在旧版本中从 typing-extensions.Guido van Rossum在昨天发布的0.900版本中为Mypy添加了支持。
回顾一下,类型收窄使用特定的表达式来推断,在一个给定的块中,一个变量具有比其定义更有限的类型。 例如,使用isinstance() 。
from __future__ import annotations
name: str | None
if isinstance(name, str):
# name must be 'str'
else:
# name must be None
类型检查器,包括Mypy,支持有限的表达式,如if isinstance(...) 。但潜在的类型收窄表达式的数量是无限的,特别是对于参数化的类型,如容器。TypeGuard 允许我们编写类型的任何表达式,并向我们的类型检查器传达它收窄类型。
一个缩小类型的函数是接受至少一个参数并返回一个bool 。我们不把返回类型标记为bool ,而是使用TypeGuard[T] ,其中True 表示第一个参数有类型T ,而False 表示没有。以这个例子为例,改编自 PEP。
from __future__ import annotations
from typing_extensions import TypeGuard
def is_str_list(value: list[object]) -> TypeGuard[list[str]]:
"""Are all list items strings?"""
return all(isinstance(x, str) for x in value)
x: list[object]
reveal_type(x)
if is_str_list(x):
reveal_type(x)
is_str_list() 如果给定的列表只包含字符串,则返回 。我们告诉Mypy这可以用 的返回类型将 的类型缩小到 。True TypeGuard value list[str]
在这个文件上运行Mypy,我们看到reveal_type() 调用的这个输出。
$ mypy --strict example.py
example.py:13: note: Revealed type is "builtins.list[builtins.object]"
example.py:15: note: Revealed type is "builtins.list[builtins.str]"
example.py:16: note: Revealed type is "builtins.list[builtins.object]"
第二个注释告诉我们,在if 块中,Mypy知道x 必须是一个字符串的列表。这使得我们可以在那里使用列表项作为str,而不会出现任何错误。 Great!
TypeGuard 是很灵活的,因为它允许我们写任意的代码来缩小表达式的范围。 它确实迫使我们把短的表达式移到独立的函数中,但这对我们代码的可读性来说往往是件好事。
因为有无限可能的表达式,类型检查器无法验证我们的表达式是否与被保护的类型一致。 所以我们需要谨慎地编写我们的TypeGuard ,并对它们进行彻底的测试。
PEP 647还展示了带有TypeVar 的通用TypeGuard 函数,但是当我试着用这些例子时,我发现Mypy 0.901还不支持它们。