我有一个
python
脚本并尝试向代码添加类型提示,以下是使用 mypy
的示例代码(没有类型提示,代码可以工作)。
values_int: list[int] = [1, 2, 3, 4]
values_str: list[str] = ["1", "2", "3", "4"]
def bar(*, x: list[int]) -> bool:
# processing
return True
def baz(*, y: list[str]) -> bool:
# processing
return True
def foo(*, values: list[int] | list[str]) -> bool:
status: bool = False
if isinstance(values[0], int):
x: list[int] = values
status = bar(x=x)
elif isinstance(values[0], str):
# case-1
# status = baz(y=values)
# case-2
y: list[str] = values
status = baz(y=y)
return status
foo(values=values_str)
错误:
# case-1
# error: Argument "y" to "baz" has incompatible type "list[int] | list[str]"; expected "list[str]"
# case-2
# error: Incompatible types in assignment (expression has type "list[int] | list[str]", variable has type "list[str]")
MyPy 似乎会要求您显式检查列表中的所有元素是否都是预期类型。我尝试了各种方法,但这只给我带来了新的错误,但这个简单的方法有效:
def foo(*, values: list[int] | list[str]) -> bool:
status: bool = False
first = type(values[0])
for x in values[1:]:
if not isinstance(x, first):
raise ValueError(f"All values must be of type {first}")
if first is int:
status = bar(x=values_int)
else:
status = baz(y=values_str)
return status
isinstance(a[b], ...)
不支持 作为类型缩小结构。
无论如何,Pyright 也不支持它。
TypeIs
:
from typing_extensions import TypeIs
def is_list_of_ints(v: list[Any]) -> TypeIs[list[int]]:
return isinstance(v[0], int)
def is_list_of_strs(v: list[Any]) -> TypeIs[list[str]]:
return isinstance(v[0], int)
def foo(*, values: list[int] | list[str]) -> None:
reveal_type(values) # list[int] | list[str]
if is_list_of_ints(values):
reveal_type(values) # list[int]
bar(x=values) # fine
elif is_list_of_strs(values):
reveal_type(values) # list[str]
baz(y=values) # fine
请注意,Mypy 1.10.0 有一个
TypeIs
的错误:它错误地确定第二个条件分支不可到达:
def foo(*, values: list[int] | list[str]) -> None:
reveal_type(values) # list[int] | list[str]
if is_list_of_ints(values):
reveal_type(values) # list[int]
bar(x=values) # fine
elif is_list_of_strs(values):
reveal_type(values) # error: statement is unreachable
baz(y=values)
TypeGuard
代替:
(游乐场链接)
from typing import TypeGuard
def is_list_of_ints(v: list[Any]) -> TypeGuard[list[int]]:
return isinstance(v[0], int)
def is_list_of_strs(v: list[Any]) -> TypeGuard[list[str]]:
return isinstance(v[0], int)