Python 将类型指定为数字列表(整数和/或浮点)?

Python 将类型指定为数字列表(整数和/或浮点)?,python,types,type-hinting,mypy,Python,Types,Type Hinting,Mypy,如何指定一个函数可以接受整数或浮点数列表 我试着用Union做了一个新类型,如下所示: num = Union[int, float] def quick_sort(arr: List[num]) -> List[num]: ... 但是,mypy不喜欢这样: quickSortLomutoFirst.py:32: error: Argument 1 to "quickSortOuter" has incompatible type List[int]; expected

如何指定一个函数可以接受整数或浮点数列表

我试着用Union做了一个新类型,如下所示:

num = Union[int, float]

def quick_sort(arr: List[num]) -> List[num]:
    ...
但是,mypy不喜欢这样:

 quickSortLomutoFirst.py:32: error: Argument 1 to "quickSortOuter" has
 incompatible type List[int]; expected List[Union[int, float]]  
是否有包含整数和浮点的类型?

从中,建议的类型提示:

本PEP不要求用户先编写导入编号,然后使用
numbers.Float
等,而是提出了一个几乎同样有效的简单快捷方式:当参数被注释为具有类型
Float
时,类型
int
的参数是可以接受的

不要为
Union
s操心。只需按
顺序[float]


编辑:感谢Michael抓住了
List
Sequence

之间的区别,您的问题的简短答案是您应该使用TypeVars或Sequence——使用
List[Union[int,float]]
实际上可能会在代码中引入错误

简言之,问题在于根据PEP 484类型系统(以及许多其他类型系统,如Java、C#……),列表是不变的。您试图使用该列表,好像它是协变的。您可以了解更多关于协方差和不变性的信息,但也许可以举一个例子来说明为什么您的代码可能是非类型安全的

考虑以下代码:

from typing import Union, List

Num = Union[int, float]

def quick_sort(arr: List[Num]) -> List[Num]:
    arr.append(3.14)  # We deliberately append a float
    return arr

foo = [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
的代码现在都会中断

或者更准确地说,尽管
int
Union[int,float]
的合法子类型,但这并不意味着
List[int]
List[Union[int,float]]
的子类型,反之亦然


如果我们对这种行为没有意见(我们对
quick\u sort
决定向输入数组中注入任意int或float没有意见),那么解决方法是使用
List[Union[int,float]]
手动注释
foo

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
,尽管只包含int,也意味着包含float。这可以防止我们在调用
quick\u sort
后错误地使用列表,从而完全避免了这个问题

在某些情况下,这可能是您想要做的。但对于这种方法,可能不是


如果我们对这种行为不满意,并且希望
quick\u sort
保留列表中最初的任何类型,那么会想到两种解决方案:

第一种是使用协变类型而不是列表,例如:

结果表明,序列或多或少类似于列表,只是它是不可变的(或者更准确地说,序列的API不包含任何让您改变列表的方法)。这让我们可以安全地避开上面的bug

第二种解决方案是更精确地键入数组,并坚持它必须包含所有int或所有float,不允许两者混合。我们可以通过以下方式实现:

这也将防止我们像上面提到的那样意外地“混合”类型


我建议使用第二种方法——它更精确一点,并且可以防止您在通过快速排序函数传递列表时丢失有关列表所包含的确切类型的信息。

我尝试过这种方法,但mypy在输入int:quickSortLomutoFirst.py:32:error:Argument 1到“quickSortOuter”时会出现此错误具有不兼容的类型列表[int];预期的List[float]@aryamcarthy——这有点微妙,但事实证明mypy实际上是正确的,它可以防止OP在代码中意外引入错误——请参阅下面的答案了解详细信息。第三种可能的解决方案和第二种不允许在列表中混合类型的替代方案是
Union[List[int],List[float]
为什么不直接使用
类型化.List[numbers.Real]
?@actual\u panda——为了进行类型检查,int和float都不是Real的子类型,因为Real是ABC,PEP 484类型系统不理解动态ABC注册。但是即使int/float是Real的子类型,List[Real]由于上面讨论的方差问题相同,仍然不起作用。执行序列[Real]或列表[t],其中
t=TypeVar('t',bound=Real)
会起作用,但前提是你可以接受任意实数,这不是每个人都想要的。但在我看来,这些细节并不重要:OP的核心问题是泛型中的差异。所以即使
isinstance(1,numbers.Real)->True
isinstance(1.1,numbers.Real)->True
类型系统没有按预期工作?这似乎是一个主要缺点。@你确定你理解了差异的根本问题以及编程语言中列表的类型吗?似乎你的困惑和公认的不直观的列表类型输入方式将得到回答。
from typing import Union, Sequence

Num = Union[int, float]

def quick_sort(arr: Sequence[Num]) -> Sequence[Num]:
    return arr
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 arr

foo = [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)