gpt4 book ai didi

python - 'Sequence of ParamSpec' 的类型提示

转载 作者:行者123 更新时间:2023-12-03 07:54:57 24 4
gpt4 key购买 nike

我有一个看起来像这样的工作函数:

from time import monotonic
from itertools import chain
from datetime import timedelta
from typing import Callable, Sequence, Any, ParamSpec

P = ParamSpec('P')


def test_function(
func: Callable[P, Any],
args: list[P.args], # Error: "args" member of ParamSpec is valid only when used with *args parameter
kwargs: list[P.kwargs] # Error: "kwargs" member of ParamSpec is valid only when used with **kwargs parameter
) -> None:
for a, kw in zip(args, kwargs):
args_str = ', '.join(chain(
(str(i) for i in a),
(f"{k}={v}" for k, v in kw.items())
))
start = monotonic()
func(*a, **kw)
print(
f"{func.__name__}({args_str}) "
f"executed in: {timedelta(seconds=monotonic() - start)}"
)

该函数的一些基本用法如下所示:

>>> def f1(a: int, b: float, *, c: int):
... return a + b + c
...
>>> test_function(f1, [(1, 2), (3, 4)], [{'c': -1}, {'c': -2}]) # essentially no type hints here for args =(
f1(1, 2, c=-1) executed in: 0:00:00.000006
f1(3, 4, c=-2) executed in: 0:00:00.000004
>>>

对我来说,它唯一的问题是它的 args 和 kwargs 参数的类型提示。如您所见,我尝试使用 ParamSpec ,但是,它给出了一个错误(我在上面的代码注释中指出),并且在阅读文档后,我意识到,不幸的是,我不能用这种类型做任何我想做的事情:

They are only valid when used in Concatenate, or as the first argument to Callable, or as parameters for user-defined Generics.

因此,我的问题是:有没有办法仍然验证包含多组参数的序列,每个参数都必须与函数签名匹配?

最佳答案

引用 typing.ParamSpec 的文档:

P.args [...] should only be used to annotate *args. P.kwargs [...] should be only be used to annotate **kwargs.

所以这对于 ParamSpec 是不可能的。

能够在其他范围内使用P.args/P.kwargs的想法是not new 。但存在一些基本问题,这些问题不太可能用 ParamSpec 来解决。实际上 PEP 612 简单地提到了根本问题,解释了为什么首先引入 ParamSpec 的原因:

The core problem here is that, by default, parameters in Python can either be called positionally or as a keyword argument. This means we really have three categories (positional-only, positional-or-keyword, keyword-only) we’re trying to jam into two categories. [...] Fundamentally, in order to capture two categories when there are some things that can be in either category, we need a higher level primitive (ParamSpec) to capture all three, and then split them out afterward.

someone in the aforementioned issue 给出了一个很好的例子:

def foo(x: int) -> None: ...
def get_callable_and_args(x: Callable[P, Any]) -> Tuple[P.args, P.kwargs]: ...

reveal_type(get_callable_and_args(foo)) # <- What should this reveal?

Should we reveal a tuple of int + empty dict? Or should we reveal an empty tuple and a singleton dict that maps x to int? [...] the existence of "positional-or-keyword" arguments in Python makes things highly ambiguous. [...]To fully resolve this kind of issue, there needs to be a way in the type system to represent positional-only, positional-or-keyword, and keyword-only parameters separately. But at that point, it's going to be a different language feature, not ParamSpec anymore.

因此,我不会屏息等待 ParamSpec 很快支持这一点。


如果我们只讨论位置参数,那么您想要的内容可以通过 TypeVarTuple 实现。 。 PEP 646特别建议它们可以用作Callable参数。

这样的事情应该是可能的:

from collections.abc import Callable
from typing import Any, TypeVarTuple

Ts = TypeVarTuple("Ts")


def test_function(func: Callable[[*Ts], Any], args: list[tuple[*Ts]]) -> None:
for a in args:
func(*a)

我说应该是因为自mypy does not fully support PEP 646 yet以来仍然很难验证.

但即便如此,也不支持关键字参数。基本上,一旦出现三个不同参数类别的组合,事情就会很快变得棘手。

关于python - 'Sequence of ParamSpec' 的类型提示,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76266392/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com