具有多个任意但相等类型的元组
Posted
技术标签:
【中文标题】具有多个任意但相等类型的元组【英文标题】:Tuple with multiple numbers of arbitrary but equal type 【发布时间】:2021-11-14 03:17:18 【问题描述】:目前,我正在检查具有多个(例如三个)任意但相同类型的元组,格式如下:
from typing import Tuple, Union
Union[Tuple[int, int, int], Tuple[float, float, float]]
我想让这个检查更通用,也允许numpy
数字类型。 IE。我尝试使用numbers.Number
:
from numbers import Number
from typing import Tuple
Tuple[Number, Number, Number]
上面的片段也允许混合类型的元组,只要一切都是数字。
我想将元组限制为相同类型的数字。
如何做到这一点?
从技术上讲,这个问题适用于 Python 和类型提示规范本身。但是,正如 cmets 中所指出的,它的处理是特定于实现的,即 MyPy 不会捕获所有边缘情况和/或不一致。就个人而言,我使用运行时检查 typeguard 进行测试,并在生产中完全停用它们。
【问题讨论】:
这有点棘手。我用TypeVar
尝试了一些东西,但后来意识到np.floating
和np.integer
是浮点数/整数,即isinstance(np.float64(3), float) -> True
。
即使在您现有的注释中,如果您传入一个元组,其中一些项目是浮点数而一些是整数mypy-play.net/…,MyPy 将遗憾地无法引发错误
@timgeb Numpy 有一个类似于常规 Python 标准库的基本类:np.number
。此外,numpy.float64
- 在大多数情况下,取决于配置、平台和编译器选项 (sys.float_info
) - 与 Python float
完全相同,所以这种事情对我来说实际上是可以的。
@AlexWaygood 我按照建议对问题添加了评论。我希望它有所帮助。静态分析并不能告诉我我想知道的一切。我正在使用 pytest
和 hypothesis 以及运行时检查来折磨我的代码。
@s-m-e 你能解释一下为什么T = TypeVar('T', int, float); Tuple[T, T, T]
不适合你吗?它应该等同于您的“手动”版本Tuple[int, int, int]
和Tuple[float, float, float]
,所以如果这些手动版本适合您,那么TypeVar
版本也应该如此。
【参考方案1】:
您可以将TypeVar
与bound
参数一起使用。它允许将类型限制为给定类型的子类型。在您的情况下,类型应该是 Number
的子类型:
from numbers import Number
from typing import TypeVar
T = TypeVar('T', bound=Number)
Tuple[T, T, T]
为什么会起作用?
TypeVar
是一个允许在类型签名中多次使用特定类型的变量。最简单的例子:
from typing import TypeVar, Tuple
T = TypeVar('T')
R = TypeVar('R')
def swap(x: T, y: R) -> Tuple[R, T]:
return y, x
静态类型检查器会推断出函数 swap
的参数应该与输出相同(请注意,返回类型 T 将与输入类型 T 相同)。
接下来,为TypeVar
引入限制可能会很有用。例如,可以将类型变量的值限制为特定类型:TypeVar('T', int, str)
。在这个答案中,我们使用了另一种带有关键字bound
的限制——它检查类型变量的值是否是给定类型的子类型(在我们的例子中,Number
,它是所有数字类型的基类在 Python 中)。
更多示例:mypy documentation 和 PEP 484。
工作测试:
from numbers import Number
from typing import Tuple, TypeVar
import numpy as np
from typeguard import typechecked
T = TypeVar('T', bound=Number)
@typechecked
def foo(data: Tuple[T, T, T]):
print('expected OK:', data)
for data in (
(1, 2, 3), # ok
(1.0, 2.0, 3.0), # ok
(1, 2.0, 3), # TypeError
(1.0, 2.0, 3), # TypeError
(1.0, 2.0, np.float64(3.0)), # ok (yes!)
(1, 2.0, np.float32(3)), # TypeError
(1, 2.0, np.uint8(3)), # TypeError
):
try:
foo( data )
except TypeError:
print('expected TypeError:', data)
【讨论】:
以上是关于具有多个任意但相等类型的元组的主要内容,如果未能解决你的问题,请参考以下文章