使用 Python 输入模块指定序列或列表的长度

Posted

技术标签:

【中文标题】使用 Python 输入模块指定序列或列表的长度【英文标题】:Specify length of Sequence or List with Python typing module 【发布时间】:2017-12-03 16:01:40 【问题描述】:

我正在尝试 Python typing 模块。

我知道指定List 的长度是有效的,如下所示*:

List[float, float, float]   # List of 3 floats <-- NOTE: this is not valid Python

有没有长列表的简写?如果我想将其设置为 10 个浮点数怎么办?

List[float * 10]   # This doesn't work.

如果可能的话,这会很方便。


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

【问题讨论】:

【参考方案1】:

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

这是来自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)

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

【讨论】:

谢谢雷蒙德,够清楚了。虽然我在这里收到的两个答案都是准确和清晰的,但我仍然不能 100% 确定提示真正需要设置长度序列输入的函数的最佳方式。我想把它放在文档字符串中并不算太糟糕,但这似乎是一种耻辱。 (我真的很喜欢 PyCharm 如何在为每种方法生成的帮助中获取这些提示) "到目前为止..." 是否有计划在某个时候在typing 模块中指定一个固定长度、可变的序列Generic【参考方案2】:

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

Tuple[float, float, float, float, float, float, float, float, float, float]

或者更好的是,使用named tuple,它同时具有索引和命名属性:

class BunchOfFloats(NamedTuple):
    foo: float
    bar: float
    baz: float
    spam: float
    ham: float
    eggs: float
    monty: float
    python: float
    idle: float
    cleese: float

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

【讨论】:

如果你使用的是元组,你也可以使用文字省略号,即Tuple[int, ...] PEP484 @TomaszBartkowiak:这与所要求的内容相反。是的,您可以通过这种方式声明一个包含单一类型的可变长度元组。但这不是固定大小 有时您需要一个固定长度的可变容器。例如。如果要将容器项初始化为 None,然后使用新值更新这些项。但容器的大小仍将保持固定。 @Matt:当然,但是没有内置的 Python 类型可以让你这样做,所以也没有类型提示。 仅供参考 @MartijnPieters 的第一个建议可以缩写为 Tuple[10 * (float, )],我觉得它相当简洁,因为它非常清楚地表达了它的目的。【参考方案3】:

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

Annotated[List[float], 3]

【讨论】:

【参考方案4】:

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

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

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

Zaffys answer 是我的最爱

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

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

接下来我尝试了Tuple[6 * (int, )],这是balu在Martijn Pieters answer的cmets中提到的

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

导致与以前相同的 Pylance 错误。 该函数的提示是:demystify: (mystery: Tuple[Tuple[Type[int], ...]]) -&gt; 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)

这解决了 Pylance 错误,并给了我一个“清晰”的功能提示:demystify: (mystery: Tuple[int, int, int, int, int, int]) -&gt; None

但就像 John Brodie 一样,我对这个解决方案并不满意。

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

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

def demystify(mystery: MysteryType):
    print(*mystery)

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

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

所以,长话短说并解释 Python 之禅:

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

【讨论】:

以上是关于使用 Python 输入模块指定序列或列表的长度的主要内容,如果未能解决你的问题,请参考以下文章

Python元组和序列区别是啥?

python 如何生成 2 1 4 3 6 5 8 7。。。的指定长度的序列?

序列类型-列表的操作

python 序列号模块

华为OD机试 - 子序列长度(Python)| 真题+思路+代码

怎么查找python列表中元素的位置