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



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

>>> 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)。因此,会有很多消息"花时间:..."。我能找到一种方法来打印 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

您可以使用类作为探查器:

import time
class ProfileRecursiveFib(object):
    def __call__(self, func):
        self.start_time = time.clock()    
        def g(x):
            return func(x)
        self.end_time = time.clock()
        print('time taken: {0}'.format(self.end_time - self.start_time))
        return g

profile = ProfileRecursiveFib()
@profile
def fib(n):
    if n is 0 or n is 1:
        return 1
    else:
        return fib(n-1) + fib(n-2)
result = fib(20)
print(result)

这将打印时间(仅一次)并产生:

time taken: 9.056212818586247e-07
10946
<</div> div class="one_answers">

我明白你期望的答案。 你的代码中有小错误,只需浏览下面的代码,

import time
def profile(f):
    start_time=time.time()
    def g(x):      
        value = f(x)
        return value
    print("time taken : %f seconds"%(time.time()-start_time))
    return g
def fib(n):
    if n is 0 or n is 1:
        return 1
    else:
        return fib(n-1) + fib(n-2)
x = profile(fib)
print(x(20))

最新更新