如何将装饰器应用于任意函数并在闭包内调用它?

How to apply decorators to an arbitrary function and call it inside the closure?

我正在学习如何在 Python 中使用装饰器,作为练习,我制作了一个装饰器函数,可以打印它所装饰的函数的执行时间。为此,我不得不在闭包函数中调用装饰函数,这是我遇到问题的地方。我的代码如下所示:

import time
import typing

T = typing.TypeVar("T")

def print_elapsed_time(func: typing.Callable[..., T]) -> typing.Callable[..., typing.Any]:
    def closure() -> T:
        start: float = time.time()
        returnValue: T = func()
        end: float = time.time()
        print(end - start)
        return returnValue
    return closure

@print_elapsed_time
def foo() -> int:
    print("foo")
    return 1

i: int = foo()
print(i)

这输出:

foo
0.0
1

如您所见,只有当 foo 有 0 个参数时,我才能在 closure 中调用 func。所以我的问题是:是否可以修改 print_elapsed_time 以便它无论装饰什么函数都可以正常工作并使用最初提供的参数调用装饰函数?我的意思是,如果 foo 接受 1 个 int 参数,我该如何修改 print_elapsed_time 以便当我写 foo(10) 时,调用 func closure 内部可以“适应”并调用 func(10)?

另一个奇怪的问题是,在第 6 行,Pylance 给我这个错误:

TypeVar "T" appears only once in generic function signature

但我需要在 print_elapsed_time 中使用 T 才能知道 return 的内容。这是 Plyance 导致的错误,因为它没有查看函数体,还是有一些方法可以修复此错误?

您可以使 closure 函数接受可变参数和可变关键字参数,并将它们传递给对 func 的调用:

def closure(*args, **kwargs) -> T:
    start: float = time.time()
    returnValue: T = func(*args, **kwargs)
    end: float = time.time()
    print(end - start)
    return returnValue

回答你的第二个问题,因为装饰器应该 return 一个 return 与传递的函数的 returning 值类型相同的函数对于装饰器,您应该将 returning 类型的 print_elapsed_time 指定为 return 类型 T 而不是 Any 的可调用类型:

def print_elapsed_time(func: typing.Callable[..., T]) -> typing.Callable[..., T]: