【问题标题】:How to use MaxLen of typing.Annotation of python 3.9?python 3.9的type.Annotation如何使用MaxLen?
【发布时间】:2021-09-27 22:13:58
【问题描述】:

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

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

V == Annotated[list[tuple[int, int]], MaxLen(10)]

但我无法完全理解MaxLen 是什么。你应该从其他地方导入一个类吗?我尝试过导入 typing.MaxLen,但似乎不存在(我使用的是 Python 3.9.6,which I think it should exist here...?)。

我认为它应该可以工作的示例代码:

from typing import List, Annotated, MaxLen

def function(foo: Annotated[List[int], MaxLen(10)]):
    # ...
    return True

在哪里可以找到MaxLen

编辑:

似乎MaxLen 是您必须创建的某种类。问题是我看不到你应该怎么做。有公开的例子吗?谁能实现这个功能?

【问题讨论】:

  • 这些只是示例,展示了可以做什么。请参阅this question 进行类似讨论。

标签: python type-hinting python-typing python-3.9


【解决方案1】:

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

这是一个示例,说明如何创建和解析自定义注释,例如 MaxLen 以帮助您入门。

首先,我们定义注解类本身。这是一个很简单的类,我们只需要存储相关的元数据,这里就是最大值:

class MaxLen:
    def __init__(self, value):
        self.value = value

现在,我们可以定义一个使用这个注解的函数,例如:

def sum_nums(nums: Annotated[List[int], MaxLen(10)]):
    return sum(nums)

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

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

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

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

from typing import Annotated, List

@check_annotations
def sum_nums_strict(nums: Annotated[List[int], MaxLen(10)]):
    return sum(nums)

以下是实际代码示例:

>>> sum_nums(nums=list(range(5)))
10
>>> sum_nums(nums=list(range(15)))
105
>>> sum_nums_strict(nums=list(range(5)))
10
>>> sum_nums_strict(nums=list(range(15)))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "annotated_test.py", line 29, in wrapped
    raise ValueError(f"Parameter '{param}' cannot have a length "
ValueError: Parameter 'nums' cannot have a length larger than 10 (got length 15).

【讨论】:

    【解决方案2】:

    Maxlen 只是他们使用的示例函数,而不是内置方法。

    【讨论】:

    • 有没有例子说明如何创建这样的功能?或类似的
    • 你想让这个函数做什么?就像我说的,这只是一个例子。
    • 正如函数所说,MaxLen 应该检查传入列表的最大长度是否为 N。我想看看这些类是什么或如何创建它们来进行此类检查。
    猜你喜欢
    • 2021-05-11
    • 2021-04-01
    • 1970-01-01
    • 2023-01-04
    • 1970-01-01
    • 1970-01-01
    • 2015-07-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多