大家好,我对 mypy 涉及工会和列表的行为有点困惑。 这是一个简化版本来帮助解释:
from typing import Union
class A:
pass
class B:
pass
def f(items, a: Union[type[A], type[B]]) -> Union[list[A], list[B]]:
return [a() for x in items] # Incompatible return value type (got "list[Union[A, B]]", expected "Union[list[A], list[B]]")
我希望列表理解能够创建
A
或 B
的同质列表。 Mypy 推断该列表是异构的,并且可以同时包含 A
和 B
类型的项目。我不明白为什么它应该因为参数 a
是 A
或 B
但在列表创建过程中不会改变。
从技术上讲,您甚至不需要列表理解(尽管在我们的用例中,我们使用几个类之一处理列表项)。以下也会出错:
def f(a: Union[type[A], type[B]]) -> Union[list[A], list[B]]:
return [a()]
我对情况的理解是否错误?有没有更好的方法来编写函数以确保输出签名(我确实想要来自该函数的几个同质列表之一)。
typechecker不知道
A
或B
中的哪一个被通过,因此它无法比a()
更具体地确定A|B
的类型。因此,列表的类型为 list[A|B]
,而不是 list[A] | list[B]
。
您应该定义一个带有约束类型变量的泛型函数。
from typing import TypeVar
T = TypeVar('T', A, B)
def f(items, t: type[T]) -> list[T]:
return [t() for x in items]
从类型检查器的角度来看,您现在已经定义了两个函数,一个是
Callable[[Any, Type[A]], list[A]]
类型,一个是Callable[[Any, Type[B]], list[B]]
类型,而不是单个Callable[[Any, Type[A]|Type[B]], list[A]|list[B]]
类型的函数。 t
的参数类型实际上决定了将调用两者中的哪一个并且应该进行类型检查:
f(something, A) # call the Callable[[Any, Type[A]], list[A]] version
f(something, B) # call the Callable[[Any, Type[B]], list[B]] version