已知长度的序列或列表的类型提示
- 2025-03-14 08:57:00
- admin 原创
- 47
问题描述:
List
我知道像下面这样指定的长度是无效的:
List[float, float, float] # List of 3 floats
或者:
List[float * 10] # List of 10 floats
这可能吗?
解决方案 1:
不能。列表是可变的、可变长度的结构。如果您需要固定长度的结构,请改用元组:
Tuple[float, float, float, float, float, float, float, float, float, float]
或者更好的是,使用命名元组,它具有索引和命名属性:
class BunchOfFloats(NamedTuple):
foo: float
bar: float
baz: float
spam: float
ham: float
eggs: float
monty: float
python: float
idle: float
cleese: float
对于固定长度的数据结构来说,列表是一种错误的数据类型。
解决方案 2:
typing.Annotated
在这里很方便。它允许您指定任意元数据来输入提示:
Annotated[list[float], 3]
刚接触Annotated
?以下是文档的片段:
如果库(或工具)遇到类型提示
Annotated[T, x]
并且没有元数据的特殊逻辑x
,它应该忽略它并简单地将该类型视为T
。
值得注意的是,mypy
有一个未完成的请求(截至 2022 年 11 月开放)用于类似的事情。与此同时,请将其视为Annotated
开发人员的可读性,而不是自动化检查(除非您开发检查工具)。
解决方案 3:
到目前为止,只有元组支持指定固定数量的字段,并且没有固定重复次数的快捷方式。
以下是来自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)
由于列表是可变的、可变长度的类型,因此使用类型声明来指定固定大小是没有任何意义的。
解决方案 4:
当我也遇到同样的问题时,看到Martijn Pieters 的回答,我并不高兴。因为我想要一种“快速”和“简单”的方法来解决这个问题。
因此我首先尝试了这里列出的其他建议。
注意:我使用 VSCode 和 Pylance 作为语言服务器
我最喜欢的是Zaffys 的回答
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]) -> None
Pylance 错误Tuple size mismatch: expected 6 but received
`a, b, c, d, e, f = mystery`
接下来我尝试了balu 在Martijn PietersTuple[6 * (int, )]
的回答评论中提到的方法
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], ...]]) -> 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]) -> None
但就像约翰·布罗迪一样,我对这个解决方案并不满意。
现在回到最初那个不想要的答案:
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) -> None
但是创建一个新的 MysteryType 可以给我所需的所有信息:(a: int, b: int, c: int, d: int, e: int, f: int, g: int)
另外,我可以在其他方法和函数中使用 MysteryType,而无需计算类型提示。
长话短说,Python 之禅的理念如下:
NamedTuples 是一个非常好的主意 - 让我们多做一些这样的事!
解决方案 5:
这个答案对类型提示没有帮助(参见上面的答案),但它有助于通过 pydantic 包进行类型检查:
from typing import Any, TypeVar, Tuple
from collections.abc import Sequence
from annotated_types import Len
from typing import Annotated
from pydantic import BaseModel
SequenceType = TypeVar('SequenceType', bound=Sequence[Any])
ShortSequence = Annotated[SequenceType, Len(max_length=2)]
class Test(BaseModel):
test: ShortSequence
print("successful!")
Test(test=[1,2])
print("fail:")
Test(test=[1,2,3])
输出:
successful!
fail:
>Traceback
ValidationError: 1 validation error for Test
test
Value should have at most 2 items after validation, not 3 [type=too_long, input_value=[1, 2, 3], input_type=list]
For further information visit https://errors.pydantic.dev/2.9/v/too_long
相关 pydantic 文档:https://docs.pydantic.dev/latest/concepts/types/#generics
扫码咨询,免费领取项目管理大礼包!