Python: Counting executing time of a recursion function with decorator

♀尐吖头ヾ 提交于 2021-02-08 07:47:39

问题


I want to write a function which behaves exactly similar to the given function, except that it prints the time consumed in executing it. Just like this:

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

This is my code,and it will print message in each function call.

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)

My code above will print a message 'taken time:...' for each fib(n-1).So there will be many many messages 'taken time:...'. Can I find a way to just print executing time of fib(20) not every executing time of fib(n-1)?


回答1:


I'm assuming your question is "how do I write profile so that it only prints one message even when I decorate a function that can call itself?"

You could keep track of which functions are currently being timed. That way, if a function calls itself, you know that you're already timing it further up the stack, and don't need to do anything for the second instance.

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

If you're using 3.X, you can cut down on mutable default argument weirdness by using the nonlocal keyword.

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



回答2:


You can use a class as your profiler:

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)

This prints time (only once) and result:

time taken: 9.056212818586247e-07
10946



回答3:


I understood the answer you expected. you have tiny mistakes in your code, just go through the below code,

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))


来源:https://stackoverflow.com/questions/29560643/python-counting-executing-time-of-a-recursion-function-with-decorator

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!