如何使用python 3.9的typing.Annotation MaxLen?

Dan*_*mar 10 python type-hinting python-typing python-3.9

我知道有一种新的类型格式,Annotated您可以在其中为函数的入口变量指定一些元数据。从文档中,您可以指定传入列表的最大长度,例如:

  • Annotated 可以与嵌套和通用别名一起使用:
T = TypeVar('T')
Vec = Annotated[list[tuple[T, T]], MaxLen(10)]
V = Vec[int]

V == Annotated[list[tuple[int, int]], MaxLen(10)]
Run Code Online (Sandbox Code Playgroud)

但我无法完全理解到底是什么MaxLen。你应该从其他地方导入一个类吗?我尝试过导入typing.MaxLen,但似乎不存在(我正在使用Python 3.9.6,我认为它应该存在于此处...?)。

我想象它应该有效的示例代码:

from typing import List, Annotated, MaxLen

def function(foo: Annotated[List[int], MaxLen(10)]):
    # ...
    return True
Run Code Online (Sandbox Code Playgroud)

哪里可以找到MaxLen

编辑:

看起来MaxLen你必须创建某种类。问题是我不知道你应该怎么做。有公开的例子吗?有人如何实现这个功能?

kik*_*s34 11

正如 AntiNeutronicPlasma 所说,Maxlen这只是一个示例,因此您需要自己创建它。

以下是如何创建和解析自定义注释的示例,以MaxLen帮助您入门。

首先,我们定义注释类本身。这是一个非常简单的类,我们只需要存储相关的元数据,在本例中为最大值:

class MaxLen:
    def __init__(self, value):
        self.value = value
Run Code Online (Sandbox Code Playgroud)

现在,我们可以定义一个使用此注释的函数,如下所示:

def sum_nums(nums: Annotated[List[int], MaxLen(10)]):
    return sum(nums)
Run Code Online (Sandbox Code Playgroud)

但如果没人检查的话,那就没什么用了。因此,一种选择可能是实现一个装饰器,在运行时检查您的自定义注释。模块中的函数get_type_hints和将会成为您最好的朋友get_origin。下面是此类装饰器的示例,它解析并强制执行类型上的注释:get_argstypingMaxLenlist

from functools import wraps
from typing import get_type_hints, get_origin, get_args, Annotated

def check_annotations(func):
    @wraps(func)
    def wrapped(**kwargs):
        # perform runtime annotation checking
        # first, get type hints from function
        type_hints = get_type_hints(func, include_extras=True)
        for param, hint in type_hints.items():
            # only process annotated types
            if get_origin(hint) is not Annotated:
                continue
            # get base type and additional arguments
            hint_type, *hint_args = get_args(hint)
            # if a list type is detected, process the args
            if hint_type is list or get_origin(hint_type) is list:
                for arg in hint_args:
                    # if MaxLen arg is detected, process it
                    if isinstance(arg, MaxLen):
                        max_len = arg.value
                        actual_len = len(kwargs[param])
                        if actual_len > max_len:
                            raise ValueError(f"Parameter '{param}' cannot have a length "
                                             f"larger than {max_len} (got length {actual_len}).")
        # execute function once all checks passed
        return func(**kwargs)

    return wrapped
Run Code Online (Sandbox Code Playgroud)

(请注意,这个特定示例仅适用于关键字参数,但您可能会找到一种方法使其也适用于普通参数)。

现在,您可以将此装饰器应用于任何函数,并且您的自定义注释将被解析:

from typing import Annotated, List

@check_annotations
def sum_nums_strict(nums: Annotated[List[int], MaxLen(10)]):
    return sum(nums)
Run Code Online (Sandbox Code Playgroud)

下面是正在运行的代码示例:

from typing import Annotated, List

@check_annotations
def sum_nums_strict(nums: Annotated[List[int], MaxLen(10)]):
    return sum(nums)
Run Code Online (Sandbox Code Playgroud)

  • PEP 593:“此 PEP 将注释类型添加到类型模块,以使用上下文特定的元数据装饰现有类型。(...)此元数据可用于静态分析或运行时。” 据我所知,目前没有 IDE 使用带注释的类型提示实现任何静态分析,对于给定的示例(列表长度、整数范围...)似乎也不可行,因为它们的值通常只能在运行时知道。 (3认同)