如何在没有 "isinstance" 的情况下将联合类型的值传递给泛型函数?
How to pass Union-typed values to generic function without "isinstance"?
我对如何在集合中存储 generics/unions 感到困惑。考虑下面的 T
可以是 bool
或 str
。我们可以将其表示为边界 TypeVar
或 Union
。当在语义上要求泛型是通过多个命名的单一类型时,差异很重要:
from typing import TypeVar, Union, List, NoReturn
T = TypeVar("T", bool, str)
T_Union = Union[bool, str]
def generic_function(event: T) -> T:
# must return same type as passed in
...
def union_function(event: T_Union) -> T_Union:
# can return any type in T_Union, not necessarily what was passed in
...
现在,如果我想在列表中存储一些可以传递给这些函数的有效值,我发现列表类型不能包含未绑定的泛型,所以我将这些值存储在 Union
其中每个元素都是 T
:
类型之一
unbound_list_of_event_types: List[T] = [True, "foo"] # error: Type variable "__main__.T" is unbound
list_of_event_types: List[T_Union] = [True, "foo"]
但是,我无法使用 Union
类型的值调用泛型函数:
generic_function(list_of_event_types[0]) # error: Value of type variable "T" of "generic_function" cannot be "Union[bool, str]"
union_function(list_of_event_types[0])
这让我觉得很奇怪,因为如果我们详尽地检查 Union
中的类型,每个函数调用都采用相同的形式,这看起来像是不必要的 isinstance-checking:
def assert_never(value: NoReturn) -> NoReturn: ...
# Exhaustively checking the types and calling the function with the *same signature*
if isinstance(list_of_event_types[0], bool):
generic_function(list_of_event_types[0])
elif isinstance(list_of_event_types[0], str):
generic_function(list_of_event_types[0])
else:
assert_never(list_of_event_types[0])
# Seems redundant when we could do this:
generic_function(list_of_event_types[0]) # type: ignore[type-var]
https://mypy-play.net/?mypy=0.931&python=3.10&gist=6133504b68fa1e74e844d0fc280ee42f
是否有更好的方法将这些值存储在一个集合中,以便将它们传递给通用函数?
使用 Union
允许输入检查:
T2 = TypeVar("T2", bound=Union[bool, str])
def generic_union_bound_function(event: T2) -> T2:
# must return same type as passed in
...
list_of_event_types: List[Union[bool, str]] = [True, "foo"]
reveal_type(generic_union_bound_function(list_of_event_types[0])) # bool | str
reveal_type(generic_union_bound_function(True)) # bool
但是如果我们想用Type[T]
推导泛型呢? (这个例子更像是各种事件类型的回调注册)。我为这种情况打开了一个 mypy 错误:https://github.com/python/mypy/issues/12115
这里的问题是 list[Union[t1,t2]]
意味着类型是 t1
或 t2
或 t1
和 t2
。 t1
和 t2
对于使用 TypeVar('T', t1, t2)
键入的内容不是有效类型,因为这意味着 't1' 或 't2' 但不是两者。在你的 bool
和 str
的例子中,可能没有任何东西既是 bool
又是 str
,但一般来说,你可以有类似
的东西
class X:
pass
class Y:
pass
class XY(X,Y):
pass
T_U = Union[X,Y]
x : T_U = X()
y : T_U = Y()
xy : T_U = XY()
现在,您可以在示例中使用的是绑定到 bool 和 str 联合的 TypeVar。如果你用它键入一个函数,它仍然会解析为输入类型等于输出类型。它还允许潜在类型是联合中两种类型的类型。然而,无论如何,这对于 bool 和 str 来说是不可能的。所以如果你尝试那个代码
T = TypeVar("T", bound= Union[bool, str])
def some_function(e:TR) -> TR:
...
a = some_function"a")
b = some_function(1)
d = a.split() # works
c = b.split() # works error: "int" has no attribute "split"
它会准确检查您想要的内容。
我对如何在集合中存储 generics/unions 感到困惑。考虑下面的 T
可以是 bool
或 str
。我们可以将其表示为边界 TypeVar
或 Union
。当在语义上要求泛型是通过多个命名的单一类型时,差异很重要:
from typing import TypeVar, Union, List, NoReturn
T = TypeVar("T", bool, str)
T_Union = Union[bool, str]
def generic_function(event: T) -> T:
# must return same type as passed in
...
def union_function(event: T_Union) -> T_Union:
# can return any type in T_Union, not necessarily what was passed in
...
现在,如果我想在列表中存储一些可以传递给这些函数的有效值,我发现列表类型不能包含未绑定的泛型,所以我将这些值存储在 Union
其中每个元素都是 T
:
unbound_list_of_event_types: List[T] = [True, "foo"] # error: Type variable "__main__.T" is unbound
list_of_event_types: List[T_Union] = [True, "foo"]
但是,我无法使用 Union
类型的值调用泛型函数:
generic_function(list_of_event_types[0]) # error: Value of type variable "T" of "generic_function" cannot be "Union[bool, str]"
union_function(list_of_event_types[0])
这让我觉得很奇怪,因为如果我们详尽地检查 Union
中的类型,每个函数调用都采用相同的形式,这看起来像是不必要的 isinstance-checking:
def assert_never(value: NoReturn) -> NoReturn: ...
# Exhaustively checking the types and calling the function with the *same signature*
if isinstance(list_of_event_types[0], bool):
generic_function(list_of_event_types[0])
elif isinstance(list_of_event_types[0], str):
generic_function(list_of_event_types[0])
else:
assert_never(list_of_event_types[0])
# Seems redundant when we could do this:
generic_function(list_of_event_types[0]) # type: ignore[type-var]
https://mypy-play.net/?mypy=0.931&python=3.10&gist=6133504b68fa1e74e844d0fc280ee42f
是否有更好的方法将这些值存储在一个集合中,以便将它们传递给通用函数?
Union
允许输入检查:
T2 = TypeVar("T2", bound=Union[bool, str])
def generic_union_bound_function(event: T2) -> T2:
# must return same type as passed in
...
list_of_event_types: List[Union[bool, str]] = [True, "foo"]
reveal_type(generic_union_bound_function(list_of_event_types[0])) # bool | str
reveal_type(generic_union_bound_function(True)) # bool
但是如果我们想用Type[T]
推导泛型呢? (这个例子更像是各种事件类型的回调注册)。我为这种情况打开了一个 mypy 错误:https://github.com/python/mypy/issues/12115
这里的问题是 list[Union[t1,t2]]
意味着类型是 t1
或 t2
或 t1
和 t2
。 t1
和 t2
对于使用 TypeVar('T', t1, t2)
键入的内容不是有效类型,因为这意味着 't1' 或 't2' 但不是两者。在你的 bool
和 str
的例子中,可能没有任何东西既是 bool
又是 str
,但一般来说,你可以有类似
class X:
pass
class Y:
pass
class XY(X,Y):
pass
T_U = Union[X,Y]
x : T_U = X()
y : T_U = Y()
xy : T_U = XY()
现在,您可以在示例中使用的是绑定到 bool 和 str 联合的 TypeVar。如果你用它键入一个函数,它仍然会解析为输入类型等于输出类型。它还允许潜在类型是联合中两种类型的类型。然而,无论如何,这对于 bool 和 str 来说是不可能的。所以如果你尝试那个代码
T = TypeVar("T", bound= Union[bool, str])
def some_function(e:TR) -> TR:
...
a = some_function"a")
b = some_function(1)
d = a.split() # works
c = b.split() # works error: "int" has no attribute "split"
它会准确检查您想要的内容。