Python装饰器以时间递归函数

Cha*_*ley 5 python recursion python-3.x python-decorators

我有一个简单的装饰器来跟踪函数调用的运行时间:

def timed(f):
    def caller(*args):
        start = time.time()
        res = f(*args)
        end = time.time()
        return res, end - start
    return caller
Run Code Online (Sandbox Code Playgroud)

这可以如下使用,并返回函数结果和执行时间的元组.

@timed
def test(n):
    for _ in range(n):
        pass
    return 0

print(test(900)) # prints (0, 2.69e-05)
Run Code Online (Sandbox Code Playgroud)

很简单.但现在我想将它应用于递归函数.将上述包装器应用于递归函数会导致嵌套元组具有每个递归调用的时间,如预期的那样.

@timed
def rec(n):
    if n:
        return rec(n - 1)
    else:
        return 0

print(rec(3)) # Prints ((((0, 1.90e-06), 8.10e-06), 1.28e-05), 1.90e-05)
Run Code Online (Sandbox Code Playgroud)

编写装饰器以便正确处理递归的优雅方法是什么?显然,如果定时函数可以包装调用:

@timed
def wrapper():
    return rec(3)
Run Code Online (Sandbox Code Playgroud)

这将给出结果和时间的元组,但我希望所有这些都由装饰器处理,这样调用者就不必担心为每个调用定义一个新函数.想法?

use*_*ica 8

这里的问题不是装饰者.问题是rec需要rec成为一种行为方式的函数,但您希望rec成为一种行为不同的函数.没有干净的方法来协调单一rec功能.

最干净的选择是停止要求rec一次做两件事.不使用装饰符号,而是指定timed(rec)其他名称:

def rec(n):
    ...

timed_rec = timed(rec)
Run Code Online (Sandbox Code Playgroud)

如果您不想要两个名称,则rec需要编写以了解装饰rec将返回的实际值.例如,

@timed
def rec(n):
    if n:
        val, runtime = rec(n-1)
        return val
    else:
        return 0
Run Code Online (Sandbox Code Playgroud)