这个问题的简短答案是您应该使用TypeVars还是Sequence-使用
List[Union[int, float]]实际上会在代码中引入错误!
简而言之,问题在于,根据PEP 484类型系统(以及许多其他类型系统-例如Java,C#…),列表是 不变的 。您正在尝试使用该列表,就好像它是
协变的一样 。您可以在此处和此处了解有关协方差和不变性的更多信息,但也许一个有用的例子说明为什么代码可能是非类型安全的。
考虑以下代码:
from typing import Union, ListNum = Union[int, float]def quick_sort(arr: List[Num]) -> List[Num]: arr.append(3.14) # We deliberately append a float return arrfoo = [1, 2, 3, 4] # type: List[int]quick_sort(foo)# Danger!!!# Previously, `foo` was of type List[int], but now# it contains a float!?
如果允许对该代码进行类型检查,那么我们就破坏了代码!现在,依赖于
foo确切类型的任何代码
List[int]都会中断。
或更准确地说,即使
int是的合法子类型
Union[int, float],也不意味着
List[int]是的子类型
List[Union[int,float]],反之亦然。
如果我们确定这种行为(我们正在与确定
quick_sort决定的任意整数或浮点数注入输入数组),修复是手动标注
foo有
List[Union[int,float]]:
foo = [1, 2, 3, 4] # type: List[Union[int, float]]# Or, in Python 3.6+foo: List[Union[int, float]] = [1, 2, 3, 4]
也就是说,预先声明
foo,尽管仅包含整数,但也应包含浮点数。这样可以防止我们在
quick_sort调用之后错误地使用列表,从而完全避免了该问题。
在某些情况下,这可能就是您想要做的。但是对于这种方法,可能不是。
如果我们 无法 确定这种行为,并希望
quick_sort保留任何类型原本在列表中,两种解决方案浮现在脑海中:
第一种是使用 协变 类型而不是列表-
例如,
Sequence:
from typing import Union, SequenceNum = Union[int, float]def quick_sort(arr: Sequence[Num]) -> Sequence[Num]: return arr
事实证明,Sequence与List差不多,除了它是不可变的(或更准确地说,Sequence的API不包含任何让您改变列表的方式)。这使我们可以安全地回避上面遇到的错误。
第二种解决方案是更精确地键入数组,并坚持 必须
包含所有int或所有浮点数,不允许两者混合使用。我们可以使用具有值限制的TypeVars来做到这一点:
from typing import Union, List, TypeVar# Note: The informal convention is to prefix all typevars with# either 'T' or '_T' -- so 'TNum' or '_TNum'.TNum = TypeVar('TNum', int, float)def quick_sort(arr: List[TNum]) -> List[TNum]: return arrfoo = [1, 2, 3, 4] # type: List[int]quick_sort(foo)bar = [1.0, 2.0, 3.0, 4.0] # type: List[float]quick_sort(foo)
这也将防止我们像上面那样意外地“混合”类型。
我建议您使用第二种方法-更为精确,这样可以防止您在通过快速排序功能传递列表时丢失有关列表所包含的确切类型的信息。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)