从 **args 推断参数类型

Inferring argument types from **args

假设我有这样的代码:

def a(n:int = 10, s:str = "")->int:
    return n 

def b(**args):
    return a(**args)

有什么方法可以告诉 python b 接受一个名为 n 的整数参数和另一个名为 s 的字符串参数?换句话说,有没有办法通过**dict传递输入?

更新:明确地说,我正在寻找一种适用于任意数量和任意类型的参数的通用解决方案。我的目标是在指定参数类型时遵循 DRY 原则。

这是一个古老的故事,讨论了很长时间。这是 mypy issue related to this problem (still open since 2018), it is even mentioned in PEP-589. Some steps were taken in right direction: python 3.11 introduced Unpack and allowed star unpacking in annotations - it was designed together with variadic generics support, see PEP-646(向后移植到 typing_extensions,但尚无 mypy 支持 AFAIC)。但只对*args有效,**kwargs建设还在等待

但是,通过额外的努力是可能的。您可以创建自己的装饰器来说服 mypy 该函数具有预期的签名 (playground):

from typing import Any, Callable, TypeVar, cast

_C = TypeVar('_C', bound=Callable)

def preserve_sig(func: _C) -> Callable[[Callable], _C]:
    def wrapper(f: Callable) -> _C:
        return cast(_C, f)
    return wrapper

def f(x: int, y: str = 'foo') -> int:
    return 1

@preserve_sig(f)
def g(**kwargs: Any) -> int:
    return f(**kwargs)

g(x=1, y='bar')
g(z=0)  # E: Unexpected keyword argument "z" for "g"

您甚至可以更改函数签名,附加或前置新参数,使用 PEP-612 (playground:

from functools import wraps
from typing import Any, Callable, Concatenate, ParamSpec, TypeVar, cast

_R = TypeVar('_R')
_P = ParamSpec('_P')


def alter_sig(func: Callable[_P, _R]) -> Callable[[Callable], Callable[Concatenate[int, _P], _R]]:
    
    def wrapper(f: Callable) -> Callable[Concatenate[int, _P], _R]:
        
        @wraps(f)
        def inner(num: int, *args: _P.args, **kwargs: _P.kwargs):
            print(num)
            return f(*args, **kwargs)
            
        return inner
    
    return wrapper

def f(x: int, y: str = 'foo') -> int:
    return 1

@alter_sig(f)
def g(**kwargs: Any) -> int:
    return f(**kwargs)

g(1, x=1, y='bar')
g(1, 2, 'bar')
g(1, 2)
g(x=1, y='bar')  # E: Too few arguments for "g"
g(1, 'baz')  # E: Argument 2 to "g" has incompatible type "str"; expected "int"
g(z=0)  # E: Unexpected keyword argument "z" for "g"