Skip to main content
 首页 » 编程设计

Python:用装饰器计算递归函数的执行时间

2025年05月04日16mayingbao

我想编写一个与给定函数完全相似的函数,除了它打印执行它所消耗的时间。 就像这样:

>>> fib = profile(fib) 
>>> fib(20) 
time taken: 0.1 sec 
10946 

这是我的代码,它会在每个函数调用中打印消息。

import time 
 
 
def profile(f): 
    def g(x): 
        start_time = time.clock() 
        value = f(x) 
        end_time = time.clock() 
        print('time taken: {time}'.format(time=end_time-start_time)) 
        return value 
    return g 
 
 
@profile 
def fib(n): 
    if n is 0 or n is 1: 
        return 1 
    else: 
        return fib(n-1) + fib(n-2) 

我上面的代码会为每个 fib(n-1) 打印一条消息“taken time:...”。所以会有很多消息“taken time:...”。我能找到一种方法来打印 fib(20) 的执行时间而不是 fib(n-1) 的每个执行时间吗?

请您参考如下方法:

我假设您的问题是“我如何编写 profile 以便它只打印一条消息,即使我装饰了一个可以调用自身的函数?”

您可以跟踪当前正在计时的函数。这样,如果一个函数调用自身,您就知道您已经在堆栈的更上层对它进行计时,并且不需要为第二个实例做任何事情。

def profile(f, currently_evaluating=set()): 
    #`currently_evaluating` will persist across all decorated functions, due to "mutable default argument" behavior. 
    #see also http://stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default-argument 
    def g(x): 
        #don't bother timing it if we're already doing so 
        if f in currently_evaluating:  
            return f(x) 
        else: 
            start_time = time.clock() 
            currently_evaluating.add(f) 
            try: 
                value = f(x) 
            finally: 
                currently_evaluating.remove(f) 
            end_time = time.clock() 
            print('time taken: {time}'.format(time=end_time-start_time)) 
            return value 
    return g 

如果您使用的是 3.X,则可以使用 nonlocal 关键字来减少可变默认参数的怪异。

def profile(f): 
    is_evaluating = False 
    def g(x): 
        nonlocal is_evaluating 
        if is_evaluating: 
            return f(x) 
        else: 
            start_time = time.clock() 
            is_evaluating = True 
            try: 
                value = f(x) 
            finally: 
                is_evaluating = False 
            end_time = time.clock() 
            print('time taken: {time}'.format(time=end_time-start_time)) 
            return value 
    return g