具有多个任意但相等类型的元组

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.floatingnp.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】:

您可以将TypeVarbound 参数一起使用。它允许将类型限制为给定类型的子类型。在您的情况下,类型应该是 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)

【讨论】:

以上是关于具有多个任意但相等类型的元组的主要内容,如果未能解决你的问题,请参考以下文章

元祖-tuple

如何键入具有相应类型的元组数组?

Apache Storm 可以用来处理具有一组动态属性的元组吗?

Python元组类型字典类型及常用操作

Python:元组类型

迭代任意长度的元组