使用Python输入模块指定Sequence或List的长度

Joh*_*die 15 python type-hinting python-3.x mypy

我正在给Python typing模块一个镜头.

我知道指定List以下*的长度是有效的:

List[float, float, float]   # List of 3 floats <-- NOTE: this is not valid Python
Run Code Online (Sandbox Code Playgroud)

是否有更长的名单的简写?如果我想将它设置为10个花车怎么办?

List[float * 10]   # This doesn't work.
Run Code Online (Sandbox Code Playgroud)

如果这是可能的,任何想法,这将是方便的.


*注意:事实证明Sequence[]以这种方式向(及其子类)提供多个参数当前不是有效的Python.此外,目前不可能以这种方式Sequence使用typing模块指定长度.

Zaf*_*ffy 43

typing.Annotated在这里可以派上用场。它允许您指定任意元数据来键入提示:

Annotated[list[float], 3]
Run Code Online (Sandbox Code Playgroud)

新的Annotated?这是文档的片段:

如果一个库(或工具)遇到 typehintAnnotated[T, x]并且对于元数据没有特殊的逻辑x,它应该忽略它并简单地将类型视为T.

值得注意的是,对于此类事情mypy有一个未完成的请求(截至 2022 年 11 月开放)。同时,考虑Annotated开发人员的可读性,而不是自动检查(除非您开发检查工具)。

  • 谢谢,很高兴知道“typing.Annotated”存在。--- 恕我直言,答案应该解释添加的整数“3”的作用就像附加到带注释的变量的注释一样。您必须编写自己的工具才能实际使用附加注释。--- 除此之外,仅添加一个整数将使附加注释变得不明确。最好创建一个能够表达上下文的结构 - 例如:“Annotated[List[float], Length[3]]” (8认同)

Mar*_*ers 8

你不能 列表是可变的,可变长度的结构。如果需要固定长度的结构,请改用元组:

Tuple[float, float, float, float, float, float, float, float, float, float]
Run Code Online (Sandbox Code Playgroud)

更好的方法是使用具有索引和命名属性的命名元组

class BunchOfFloats(NamedTuple):
    foo: float
    bar: float
    baz: float
    spam: float
    ham: float
    eggs: float
    monty: float
    python: float
    idle: float
    cleese: float
Run Code Online (Sandbox Code Playgroud)

对于固定长度的数据结构,列表只是错误的数据类型。

  • 有时您需要一个固定长度的可变容器。例如,如果您想将容器项目初始化为“无”,但随后使用新值更新项目。但容器的尺寸仍保持固定。 (11认同)
  • @TomaszBartkowiak:这与所要求的“相反”。是的,您可以通过这种方式声明一个包含单一类型的可变长度元组。但这*不是固定大小*。 (6认同)
  • 如果您使用元组,您还可以使用文字省略号,即按照 [PEP484](https://docs.python.org/3/library/typing.html#typing) 的“Tuple[int, ...]”。元组) (4认同)
  • @Matt:当然,但是没有内置的Python类型可以让你这样做,所以也没有类型提示。 (2认同)

小智 6

当也面临同样的问题时,我很高兴看到Martijn Pieters 的回答。因为我想要一种“快速”和“简单”的方法来解决这个问题。

所以我首先尝试了这里列出的其他建议。

注意:我使用 VSCode 和 Pylance 作为语言服务器

扎菲的回答是我最喜欢的

def demystify(mystery: Annotated[Tuple[int], 6]):
    a, b, c, d, e, f = mystery
    print(a, b, c, d, e, f)
Run Code Online (Sandbox Code Playgroud)

该函数的提示如下所示:此外,我还收到该行的demystify: (mystery: Tuple[int]) -> None Pylance 错误Tuple size mismatch: expected 6 but receiveda, b, c, d, e, f = mystery

接下来我尝试了balu 在Martijn Pieters 答案Tuple[6 * (int, )]的评论中提到的

def demystify(mystery: Tuple[6 * (int,)]):
    a, b, c, e, f, g = mystery
    print(a, b, c, e, f, g)
Run Code Online (Sandbox Code Playgroud)

导致与之前相同的 Pylance 错误。该函数的提示是这样的:demystify: (mystery: Tuple[Tuple[Type[int], ...]]) -> None

回到写下预期长度:

def demystify(mystery: Tuple[int, int, int, int, int, int]):
    a, b, c, e, f, g = mystery
    print(a, b, c, e, f, g)
Run Code Online (Sandbox Code Playgroud)

这解决了 Pylance 错误,并给了我一个“清晰”的函数提示:demystify: (mystery: Tuple[int, int, int, int, int, int]) -> None

但就像约翰·布罗迪一样,我对这个解决方案并不满意。

现在回到最初不需要的答案:

class MysteryType(NamedTuple):
    a: int
    b: int
    c: int
    d: int
    e: int
    f: int
    g: int

def demystify(mystery: MysteryType):
    print(*mystery)
Run Code Online (Sandbox Code Playgroud)

函数提示现在看起来更加神秘:demystify: (mystery: MysteryType) -> None但是创建一个新的 MysteryType 为我提供了我需要的所有信息:(a: int, b: int, c: int, d: int, e: int, f: int, g: int)

我还可以在其他方法和函数中使用 MysteryType,而无需计算类型提示。

因此,长话短说,解释一下 Python 的禅宗:

NamedTuples 是一个非常棒的想法——让我们多做一些这样的事情吧!

  • `Annotated[Tuple[int], 6]` 表示具有单个 int 的元组(以 6 作为元数据)。Zaffy 的答案是“Annotated[List[int], 6]”,它是一个任意的整数列表(其中 6 作为元数据)。理想情况下,类型检查器能够读取 6 以了解您想要一个固定大小的列表,但这不是指定它的标准方法。 (3认同)

Ray*_*ger 5

到目前为止,只有元组支持指定固定数量的字段,并且对于固定数量的重复没有捷径。

这是typing模块的定义和文档字符串:

class Tuple(tuple, extra=tuple, metaclass=TupleMeta):
    """Tuple type; Tuple[X, Y] is the cross-product type of X and Y.

    Example: Tuple[T1, T2] is a tuple of two elements corresponding
    to type variables T1 and T2.  Tuple[int, float, str] is a tuple
    of an int, a float and a string.

    To specify a variable-length tuple of homogeneous type, use Tuple[T, ...].
    """

    __slots__ = ()

    def __new__(cls, *args, **kwds):
        if _geqv(cls, Tuple):
            raise TypeError("Type Tuple cannot be instantiated; "
                            "use tuple() instead")
        return _generic_new(tuple, cls, *args, **kwds)
Run Code Online (Sandbox Code Playgroud)

由于列表是可变的,可变长度的类型,因此使用类型声明来指定固定大小没有任何意义。

  • `由于列表是一种可变的、可变长度的类型,因此使用类型声明来指定固定大小是没有任何意义的。`人们可能希望保留一个固定大小的列表只是为了可变性(即能够就地更新元素)。在我看来,这是完全有道理的。一个简单的示例:表示范围“[start, stop]”的列表,其中边界可更新。 (3认同)
  • “到目前为止......”是否有计划在某个时候在 `typing` 模块中指定一个固定长度的可变序列 `Generic`? (2认同)