用于具有多种类型的通用可变元组/固定长度序列的 Python 类型提示

kma*_*man 8 python type-hinting data-structures python-typing

我目前正在努力向项目添加类型提示,但不知道如何做到这一点。我有一个列表列表,其中嵌套列表包含两个 int 和 float 类型的元素。嵌套列表的第一个元素始终是 int,第二个元素始终是 float。

my_list = [[1000, 5.5], [1432, 2.2], [1234, 0.3]]
Run Code Online (Sandbox Code Playgroud)

我想对它进行类型注释,以便在 for 循环或循环理解中解包内部列表以保留类型信息。我可以将内部列表更改为元组,并得到我正在寻找的内容:

def some_function(list_arg: list[tuple[int, float]]): pass

Run Code Online (Sandbox Code Playgroud)

但是,我需要内部列表是可变的。有没有一种好的方法可以对列表执行此操作?我知道像 Sequence 和 Collection 这样的抽象类不支持多种类型。

Tal*_*uck 4

我认为这个问题强调了静态类型 Python 和动态类型 Python 之间的根本区别。对于习惯动态类型 Python(或 Perl 或 JavaScript 或任何其他脚本语言)的人来说,列表中具有不同的数据类型是完全正常的。它方便、灵活,并且不需要您定义自定义数据类型。然而,当您引入静态类型时,您就进入了一个需要更严格设计的更严格的盒子。

正如其他几个人已经指出的那样,列表的类型注释要求列表的所有元素都是相同的类型,并且不允许您指定长度。您不应该将此视为类型系统的缺点,而应该考虑该缺陷存在于您自己的设计中。您真正需要的是一个具有两个数据成员的类。第一个数据成员已命名0且具有类型int,第二个数据成员已命名1且具有类型float。作为您的朋友,我建议您定义一个适当的类,并为这些数据成员提供有意义的名称。由于我不确定您的数据类型代表什么,因此我将编造名称以供说明。

class Sample:
    def __init__(self, atomCount: int, atomicMass: float):
        self.atomCount = atomCount
        self.atomicMass = atomicMass
Run Code Online (Sandbox Code Playgroud)

这不仅解决了打字问题,还大大提高了可读性。您的代码现在看起来更像这样:

my_list = [Sample(1000, 5.5), Sample(1432, 2.2), Sample(1234, 0.3)]

def some_function(list_arg: list[Sample]): pass
Run Code Online (Sandbox Code Playgroud)

我确实认为值得强调 Stef 的评论,它指出了这个问题。给出的答案强调了与此相关的两个有用的功能。

首先,从Python 3.7开始,您可以将一个类标记为数据类,它将自动生成诸如__init__(). 使用装饰器,类Sample看起来像这样@dataclass

from dataclasses import dataclass

@dataclass
class Sample:
    atomCount: int
    atomicMass: float
Run Code Online (Sandbox Code Playgroud)

该问题的另一个答案提到了一个名为 recordclass 的 PyPi 包,它说它基本上是一个可变的namedtuple. 键入的版本称为RecordClass

from recordclass import RecordClass

class Sample(RecordClass):
    atomCount: int
    atomicMass: float
Run Code Online (Sandbox Code Playgroud)