是否有使用Python输入模块为复合类型创建类型别名的更好方法?

问题描述 投票:2回答:1

我有一个带有一个参数的函数,它应该以intNone作为参数。有几种方法可以为这种复合类型创建类型别名:

# test.py
import typing

IntOrNone_1 = typing.TypeVar('IntOrNone_1', int, None)
IntOrNone_2 = typing.Union[int, None]


def my_func1(xyz: IntOrNone_1):
    return xyz

def my_func2(xyz: IntOrNone_2):
    return xyz


my_func1(12)
my_func1(None)
my_func1(13.7)
my_func1('str')

my_func2(12)
my_func2(None)
my_func2(13.7)
my_func2('str')

两种方法都按照我的预期进行,但是,相应的qazxsw poi误差略有不同,但基本上具有相同的含义。

test.py:14:错误:“my_func1”的类型变量“IntOrNone_1”的值不能为“float”

test.py:15:错误:“my_func1”的类型变量“IntOrNone_1”的值不能是“str”

test.py:19:错误:参数1到“my_func2”具有不兼容的类型“float”;预期“可选[int]”

test.py:20:错误:参数1到“my_func2”具有不兼容的类型“str”;预期“可选[int]”

我倾向于使用第二种方法,因为它还会报告哪个参数导致了错误。

这两种方法确实是等价的,正如我想的那样,还是其中之一?

python typing typechecking
1个回答
3
投票

这两种方法远非等效。您应该避免将TypeVars视为仅仅是别名 - 相反,它们是您想要使函数通用时使用的更特殊的形式。

最简单的解释一个“泛型函数”是什么。假设您要编写一个接受某个对象(任何对象!)的函数,并返回另一个完全相同类型的对象。你会怎么做?

我们可以做的一种方法是尝试使用mypy

object

这让我们接近,因为我们的def identity(x: object) -> object: return x 函数至少可以接受任何东西(因为所有类型都继承自Python中的identity)。但是,这个解决方案存在缺陷:如果我们传入一个int,我们会退回object,这不是我们想要的。

相反,我们需要的是类型检查器理解这两种类型之间存在“关系”的方法。这正是object派上用场的地方:

TypeVar

在这种情况下,我们的TypeVar'T'充当“占位符”,可以绑定到我们想要的任何类型。因此,如果我们做T = TypeVar('T') def identity(x: T) -> T: return x identity(3)将绑定到T - 所以类型检查器将因此理解返回类型也必须是int

如果我们在参数类型提示中多次使用int,则类型检查器将确保每次类型都相同。


那么,下面的表达是什么?

T

好吧,事实证明,为我们的特殊占位符类型添加约束有时会很有用。例如,你已经约束了IntOrNone_1 = typing.TypeVar('IntOrNone_1', int, None) ,因此它只能绑定到IntOrNone_1int,而不能绑定其他类型。


最后,回答你的最后一个问题:在你给出的例子中,你绝对应该使用Union,而不是TypeVars。

无论您使用Union还是联盟的类型别名都是个人品味的问题,但如果您不需要这种“占位符”或“通用”行为,则不应使用TypeVars。

如果您想了解有关如何使用TypeVars的更多信息,mypy文档有一个None。它涵盖了我跳过的几个方面,包括如何制作泛型类(不仅仅是泛型函数)。

© www.soinside.com 2019 - 2024. All rights reserved.