为返回采用 args 和 kwargs 的函数的函数键入注释

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

以下示例代码在运行时有效,但不被

mypy --strict
接受:

from typing import Any, Callable, TypeVar

TypeT = TypeVar('TypeT')


def log_call(msg: str) -> Callable[..., TypeT]:
    def do_call(func: Callable[..., TypeT], *args: Any, **kwargs: Any) -> TypeT:
        print(msg)
        return func(*args, **kwargs)

    return do_call


def double(val: int) -> int:
    return 2 * val


def plus(a: int, b: int) -> int:
    return a + b


def plus_plus(a: int, b: int, c: int) -> int:
    return a + b + c


result_1 = log_call('hi')(double, 1)
result_2 = log_call('hi')(plus, 2, 3)
result_3 = log_call('hi')(plus_plus, 2, 3, 4)

result_4 = log_call('hi')(double, val=1)

print(result_1)
print(result_2)
print(result_3)
print(result_4)

mypy
输出:

test.py:26: error: Need type annotation for 'result_1'
test.py:27: error: Need type annotation for 'result_2'
test.py:28: error: Need type annotation for 'result_3'
test.py:30: error: Need type annotation for 'result_4'
test.py:32: error: Cannot determine type of 'result_1'
test.py:33: error: Cannot determine type of 'result_2'
test.py:34: error: Cannot determine type of 'result_3'
test.py:35: error: Cannot determine type of 'result_4'

现在我不想向

result*
变量添加类型注释,而是想调整函数的类型注释,以便可以推断出其他函数。这是我的尝试:

from typing import Any, Callable, TypeVar

TypeT = TypeVar('TypeT')


def log_call(msg: str) -> Callable[[Callable[..., TypeT], Any, Any], TypeT]:
    def do_call(func: Callable[..., TypeT], *args: Any, **kwargs: Any) -> TypeT:
        print(msg)
        return func(*args, **kwargs)

    return do_call


def double(val: int) -> int:
    return 2 * val


def plus(a: int, b: int) -> int:
    return a + b


def plus_plus(a: int, b: int, c: int) -> int:
    return a + b + c


result_1 = log_call('hi')(double, 1)
result_2 = log_call('hi')(plus, 2, 3)
result_3 = log_call('hi')(plus_plus, 2, 3, 4)

result_4 = log_call('hi')(double, val=1)

print(result_1)
print(result_2)
print(result_3)
print(result_4)

但是现在参数的数量已经无法容纳了:

test.py:26: error: Too few arguments
test.py:28: error: Too many arguments
test.py:30: error: Unexpected keyword argument "val"

我想我正在寻找类似的东西

def log_call(msg: str) -> Callable[[Callable[..., TypeT], ...], TypeT]:

但是这种语法不合法。

有什么办法可以解决这个问题吗?

python mypy python-typing
1个回答
1
投票

解决此问题的最简单方法是使

log_call
成为可调用对象。这将类型注释与包装器和实际调用分开。此外,从 Python 3.10 开始,可以使用
ParamSpec
来注释输入参数 (
*args
&
**kwargs
)。这是一个通过
mypy --strict
并有效的完整示例:

from typing import Callable, ParamSpec

P = ParamSpec("P")


class log_call:
    def __init__(self, msg: str) -> None:
        self.msg = msg

    def __call__(
        self, func: Callable[P, int], *args: P.args, **kwargs: P.kwargs
    ) -> int:
        print(self.msg)
        return func(*args, **kwargs)


def double(val: int) -> int:
    return 2 * val


def plus(a: int, b: int) -> int:
    return a + b


def plus_plus(a: int, b: int, c: int) -> int:
    return a + b + c


result_1 = log_call("hi")(double, 1)
result_2 = log_call("hi")(plus, 2, 3)
result_3 = log_call("hi")(plus_plus, 2, 3, 4)
result_4 = log_call("hi")(double, val=1)

print(result_1)
print(result_2)
print(result_3)
print(result_4)
print(result_4)
print(result_4)
© www.soinside.com 2019 - 2024. All rights reserved.