与打字一起使用的可重用约束类型。带注释
项目描述
注释类型
PEP-593添加typing.Annotated
为将特定于上下文的元数据添加到现有类型的一种方式,并指定
任何工具或库Annotated[T, x]
都应将其视为T
没有特殊逻辑的x
.
这个包提供了元数据对象,可用于表示常见约束,例如标量值和集合大小的上限和下限、Predicate
运行时检查的标记
以及我们打算如何解释这些元数据的非规范性描述。在某些情况下,我们还注意到不需要此包的替代表示。
安装
pip install annotated-types
例子
from typing import Annotated
from annotated_types import Gt, Len, Predicate
class MyClass:
age: Annotated[int, Gt(18)] # Valid: 19, 20, ...
# Invalid: 17, 18, "19", 19.0, ...
factors: list[Annotated[int, Predicate(is_prime)]] # Valid: 2, 3, 5, 7, 11, ...
# Invalid: 4, 8, -2, 5.0, "prime", ...
my_list: Annotated[list[int], 0:10] # Valid: [], [10, 20, 30, 40, 50]
# Invalid: (1, 2), ["abc"], [0] * 20
your_set: Annotated[set[int], Len(0, 10)] # Valid: {1, 2, 3}, ...
# Invalid: "Well, you get the idea!"
文档
虽然annotated-types
避免了运行时检查性能,但用户不应构造无效组合,例如MultipleOf("non-numeric")
or Annotated[int, Len(3)]
。如果下面描述的元数据对象与不兼容的类型一起使用 - 或出于任何其他原因,下游实现者可能会选择引发错误、发出警告、静默忽略元数据项等!
Gt、Ge、Lt、Le
表示可排序值的包容性和/或排斥性界限 - 可以是数字、日期、时间、字符串、集合等。请注意,边界值不必与被注释的类型相同,只要它们可以进行比较:Annotated[int, Gt(1.5)]
很好,例如,并暗示该值是一个整数 x 使得x > 1.5
. 没有为特殊值指定解释,例如nan
.
对于希望避免运行时依赖于包的用户,我们建议实现者也可以将其解释functools.partial(operator.le, 1.5)
为等同于,。Gt(1.5)
annotated-types
明确地说,这些类型具有以下含义:
Gt(x)
- 值必须是“大于”x
- 等于排他最小值Ge(x)
- 值必须“大于或等于”x
- 等于包含最小值Lt(x)
- 值必须是“小于”x
- 等于独占最大值Le(x)
- 值必须“小于或等于”x
- 等于包含最大值
间隔
Interval(gt, ge, lt, le)
允许您使用单个元数据对象指定上限和下限。None
属性应该被忽略,非None
属性按照上面的单一界限处理。
倍数
MultipleOf(multiple_of=x)
可以用两种方式解释:
- Python 语义,暗示
value % multiple_of == 0
,或 - JSONschema 语义,其中
int(value / multiple_of) == value / multiple_of
.
我们鼓励用户注意这两种常见的解释及其不同的行为,特别是因为非常大或非整数的数字很容易由于浮点不精确而导致无声数据损坏。
我们鼓励图书馆仔细记录他们实施的解释。
伦
Len()
意味着min_inclusive <= len(value) < max_exclusive
。我们建议库以slice
与 相同的方式解释对象Len()
,使以下所有情况等效:
Annotated[list, :10]
Annotated[list, 0:10]
Annotated[list, None:10]
Annotated[list, slice(0, 10)]
Annotated[list, Len(0, 10)]
Annotated[list, Len(max_exclusive=10)]
当然,您可以描述三个或更多元素的列表(Len(min_inclusive=3)
)、四个、五个或六个元素(Len(4, 7)
-注意排他最大值!)或恰好
八个元素(Len(8, 9)
)。
实现者:注意 Len() 应该总是有一个整数值
min_inclusive
,但slice
对象也可以有start=None
。
时区
Timezone
可以与 adatetime
或 a一起使用time
来表示允许使用哪些时区。Annotated[datetime, Timezone(None)]
必须是一个天真的日期时间。
Timezone[...]
(文字省略号)表示允许任何时区感知的日期时间。你也可以传递一个特定的时区字符串或timezone
对象,例如Timezone(timezone.utc)
or
Timezone("Africa/Abidjan")
来表示你只允许一个特定的时区,尽管我们注意到这通常是脆弱设计的症状。
谓词
Predicate(func: Callable)
表示func(value)
对于有效值是真实的。用户应该更喜欢上面的静态可检查元数据,但如果您需要任意运行时谓词的全部功能和灵活性......这里就是。
我们为常见的字符串约束提供了一些预定义的谓词:
IsLower = Predicate(str.islower)
、IsUpper = Predicate(str.isupper)
和
IsDigit = Predicate(str.isdigit)
。鼓励用户使用可以给予特殊处理的方法,并避免像lambda s: s.lower()
.
一些库可能具有处理已知或可理解谓词的特殊逻辑,例如通过检查str.isdigit
并使用它的存在来调用自定义逻辑以强制执行仅数字字符串,并自定义一些生成的外部模式。
对于引发异常的谓词,我们没有指定预期的行为。例如Annotated[int, Predicate(str.isdigit)]
,可能会默默地跳过无效的约束,或者静态地引发错误;或者它可能会尝试调用它,然后传播或丢弃产生的
TypeError: descriptor 'isdigit' for 'str' objects doesn't apply to a 'int' object
异常。我们鼓励图书馆记录他们选择的行为。
将下游类型与GroupedMetadata
实施者可以选择提供一个方便的包装器,将多条元数据分组。这有助于减少用户的冗长和认知开销。例如,像 Pydantic 这样的实现者可能会提供一个Field
orMeta
类型来接受关键字参数并将它们转换为低级元数据:
from dataclasses import dataclass
from typing import Iterator
from annotated_types import GroupedMetadata, Ge
@dataclass
class Field(GroupedMetadata):
ge: int | None = None
description: str | None = None
def __iter__(self) -> Iterator[object]:
# Iterating over a GroupedMetadata object should yield annotated-types
# constraint metadata objects which describe it as fully as possible,
# and may include other unknown objects too.
if self.ge is not None:
yield Ge(self.ge)
if self.description is not None:
yield Description(self.description)
GroupedMetadata
使用注释类型约束的库应该通过迭代对象并将结果视为已在类型中“解包”来检查并解包它Annotated
。应该将相同的逻辑应用于PEP 646Unpack
类型,以便一致地处理Annotated[T, Field(...)]
和Annotated[T, Unpack[Field(...)]]
。Annotated[T, *Field(...)]
我们自己的annotated_types.Interval
类是 a GroupedMetadata
,它把自己解包成Gt
,Lt
等等,所以这不是一个抽象的问题。
使用元数据
我们不打算对元数据和约束是如何使用的,但作为一个如何从类型注释中解析约束的示例,请参见我们在test_main.py
.
由实施者决定如何使用此元数据。您可以将元数据用于运行时类型检查、生成模式或生成示例数据以及其他用例。
设计与历史
这个包是由 Pydantic 和 Hypothesis 的维护者在 PyCon 2022 sprint 中设计的,目的是让最终用户尽可能容易地提供更多信息量的注释供运行时库使用。
它是故意最小的,并且遵循 PEP-593 允许相当大的下游自由裁量权(如果有的话!)他们选择支持什么(如果有的话!)。尽管如此,我们希望保持简单并仅涵盖最常见的用例将为用户和维护人员提供我们所能提供的最佳体验。如果您想为您的类型提供更多约束 - 跟随我们的领导,通过定义它们并在下游记录它们!
项目详情
下载文件
下载适用于您平台的文件。如果您不确定要选择哪个,请了解有关安装包的更多信息。