如何缩小工会名单?

How to narrow a list of unions?

我有一个 list[A | B] 类型的变量,它可以包含一个混合列表(如 [A(), B(), A()])。

如果我后来遇到一些极端情况并且我想确保所有元素实际上都是 A 类型,我可以 assert isinstancefor 循环中:

def f(mylist: list[A | B]):
    ...
    ...
    for el in mylist:
        assert isinstance(el, A)
    # Now, I'm sure that mylist is actually `list[A]`
    # How do I tell that to the type checker?

for 循环之后,如果我 reveal_type(mylist) 它仍然显示 list[A|B]。我也尝试了 assert all(isinstance(el, A) for el in mylist) 而不是显式循环,但 mypy 仍然无法缩小它。这可能吗?或者我必须在这里使用 cast 吗?

Or do I have to use cast here?

使用 Python 3.10 可以缩小列表范围的 cast 的唯一替代方法是使用 TypeGuard,此代码:

from typing import TypeGuard


def is_str_list(val: list[int | str]) -> TypeGuard[list[str]]:
    """Determines whether all objects in the list are strings"""
    return all(isinstance(x, str) for x in val)


def is_int_list(val: list[int | str]) -> TypeGuard[list[int]]:
    """Determines whether all objects in the list are ints"""
    return all(isinstance(x, int) for x in val)


def f(mylist: list[int | str]):

    if is_str_list(mylist):
        reveal_type(mylist)

    elif is_int_list(mylist):
        reveal_type(mylist)

    reveal_type(mylist)

缩小列表的类型而不使用 cast:

narrow_collection.py:17: note: Revealed type is "builtins.list[builtins.str]"
narrow_collection.py:21: note: Revealed type is "builtins.list[builtins.int]"
narrow_collection.py:24: note: Revealed type is "builtins.list[Union[builtins.int, builtins.str]]"