通用关键字参数类型注释

问题描述 投票:0回答:1

我想为

fn1, fn2
kwargs
添加类型注释:

class Foo:
    def __init__(self, fn1, fn2, **kwargs):
        self.res1 = fn1(**kwargs)
        self.res2 = fn2(**kwargs)

我想将

kwargs
绑定到
fn1
fn2
输入参数以避免出现以下情况:

# fails on fn2 as kwargs doesnt have a key y
Foo(fn1=lambda x: x+1, fn2=lambda x,y:x+y, x=1).

是否有可能在当前的 Python(3.11 或 3.12)中正确注释它?

例如,如果我的函数只有一个参数,我会使用

typing.TypeVar
,如下所示:

from typing import Callable, TypeVar

T = TypeVar('T')


class Foo:
    def __init__(self, fn1: Callable[[T], T], fn2: Callable[[T], T], x: T):
        self.res1 = fn1(x)
        self.res2 = fn2(x)


# mypy typecheck fail when running mypy file.py
Foo(lambda x, y: x+1, lambda y: y*2, x=5)

# mypy typecheck pass when running mypy file.py
Foo(lambda x: x+x, lambda y: y*2, x='a')
Foo(lambda x: x+x, lambda y: y*2, x=3.14)

mypy file.py
输出:

file.py:13: error: Cannot infer type of lambda  [misc]
file.py:13: error: Argument 1 to "Foo" has incompatible type "Callable[[Any, Any], Any]"; expected "Callable[[int], int]"  [arg-type]
Found 2 errors in 1 file (checked 1 source file)
python mypy type-annotation
1个回答
0
投票

这几乎可以通过

ParamSpec
完成。然而,有一个小问题,要使用 ParamSpec,您还必须为您的函数允许
*args
。您无法使用 ParamSpec 分离
kwargs
args

from typing import ParamSpec, TypeVar

from collections.abc import Callable

# the return type
R = TypeVar('R')
# param type
P = ParamSpec('P')

class Foo:
    # you need *args
    def __init__(
        self,
        fn1: Callable[P, R],
        fn2: Callable[P, R],
        *args: P.args,
        **kwargs: P.kwargs,
    ) -> None:
        self.res1 = fn1(*args, **kwargs)
        self.res2 = fn2(*args, **kwargs)

现在可以了:

Foo(
    fn1=lambda x: x+1,
    fn2=lambda x: x+2,
    x=1,
)

这会产生一条很好的错误消息:

“Foo”出现意外的关键字参数“y”

Foo(
    fn1=lambda x: x+1,
    fn2=lambda x: x+2,
    y=1,
)

这会产生一个非常复杂的错误消息

Foo(
    fn1=lambda x: x+1,
    fn2=lambda y: y+2,
    y=1,
)
© www.soinside.com 2019 - 2024. All rights reserved.