Search code examples
pythonpython-3.xfunctionrecursiondecorator

Decorators for recursion function


I want to write a decorator that will compute runtime of function that compute fibonachi number of 5(fib(5)) 10 times and will print a medium runtime value. But my decorator returns an error. How to fix this problem?

import functools
import time

def trace(times):
    def cache(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            total = 0
            for i in range(times):
                start = time.time()
                func(*args, **kwargs)
                end = time.time()
                total += (end - start)
            print('Medium value is {}'.format(total / times))
            return func
        return wrapper
    return cache

@trace(times=10)
def fib(num):
    if num < 2:
        return num
    return fib(num - 1) + fib(num - 2)

fib(5)

Solution

  • First, instead of returning func in the wrapper return the result of it.

    We have to make sure the tracing only happens for the first call to fib and none of the recursive calls. Let's use a boolean traced argument:

    import functools
    import time
    
    def trace(times):
        def cache(func):
            @functools.wraps(func)
            def wrapper(*args, traced=True, **kwargs):
                if not traced:
                    return func(*args, **kwargs)
                total = 0
                for i in range(times):
                    start = time.time()
                    result = func(*args, **kwargs)
                    end = time.time()
                    total += (end - start)
                    print(f'run {i}, time is {total}')
                print(f'Mean value is {total / times}')
                return result
            return wrapper
        return cache
    
    
    @trace(times=10)
    def fib(num, traced=True):
        if num < 2:
            return num
        return fib(num - 1, traced=False) + fib(num - 2, traced=False)
    
    
    fib(5)
    

    traced is True by default so any call to fib(n) will be traced (no need for user to be aware of the traced argument). However the recursive calls all have traced=False therefore they don't trigger the loop in the decorator.