我想为
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)
这几乎可以通过
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,
)