装饰递归函数

3 投票
2 回答
568 浏览
提问于 2025-04-16 16:50

我写了一个装饰器,可以用来计算某个函数的执行时间。这个装饰器对任何函数都能很好地工作,但对递归函数就不行了。

装饰器代码:

def tictoc(repeats=3, loops=1):
    def t(func):
        from functools import partial, wraps
        import timeit
        @wraps(func)
        def timer(*args, **kargs):
            elapsed = timeit.repeat(partial(func, *args, **kargs), repeat = repeats, number=loops)
            mine = min(elapsed)
            print "%s finished in %.5fs (%d loops, %d times) with %.5fs per loop" % (func.__name__, mine, loops, repeats, mine/loops)
        return timer
    return t

这个递归函数是基本的斐波那契算法。

@tictoc()
def fib(i):
    return ( 0 if i == 0 else
             1 if i == 1 else
             fib(i-1) + fib(i-2) )
fib(15)

程序出现了以下错误:

fib finished in 0.00000s (1 loops, 3 times) with 0.00000s per loop
fib finished in 0.00000s (1 loops, 3 times) with 0.00000s per loop
fib finished in 0.00000s (1 loops, 3 times) with 0.00000s per loop
Traceback (most recent call last):
  File "decor.py", line 61, in <module>
    [fib(x) for x in range(1,50)]
  File "/home/grout/Dropbox/Python/tictoc.py", line 7, in timer
    elapsed = timeit.repeat(partial(func, *args, **kargs), repeat = repeats, number=loops)
  File "/usr/lib/python2.7/timeit.py", line 233, in repeat
    return Timer(stmt, setup, timer).repeat(repeat, number)
  File "/usr/lib/python2.7/timeit.py", line 221, in repeat
    t = self.timeit(number)
  File "/usr/lib/python2.7/timeit.py", line 194, in timeit
    timing = self.inner(it, self.timer)
  File "/usr/lib/python2.7/timeit.py", line 100, in inner
    _func()
  File "decor.py", line 59, in fib
    fib(i-1) + fib(i-2) )
TypeError: unsupported operand type(s) for +: 'NoneType' and 'NoneType'

我不明白的是,为什么装饰器能执行几次后就失败了。希望能得到一些帮助。

2 个回答

6

你写的装饰器函数没有返回任何东西。所以虽然 fib 应该返回一个整数,但你包裹后的 fib 实际上返回的是 None。它能正常工作几次是因为有一些递归调用没有检查 fib 的结果,但当需要结果的时候(比如要把两个结果加在一起),就会出现错误。

0

我想我决定就用这个了

def tictoc(func, repeats=3, loops=100, *args, **kargs):
    elapsed = timeit.repeat(lambda: func(*args, **kargs), repeat = repeats, number = loops)
    mine = min(elapsed)
    return "%s finished in %.5fs (%s loops, repeated %s times): %.5fs best time per loop"         %(func.__name__, mine, loops, repeats, mine/loops)

撰写回答