Python memoize decorator
偶然看 Python 代码,看到这么一个叫memoize decorator 的东西,你会发现它非常有用。
看下面的例子,我们最常见的 fibonacci 数列的递归实现。
~% python >>> def fib(n): ... if n in (0, 1): ... return n ... else: ... return fib(n-1)+fib(n-2) ... >>> import timeit >>> timeit.timeit("fib(5)", "from __main__ import fib") 3.946546792984009 >>> timeit.timeit("fib(7)", "from __main__ import fib") 10.944302082061768
很慢,不是么?你当然可以把它的实现改成迭代,但是,如果前提是我们不对fib()函数本身做任何代码修改的话,我们还有别的方法去改进它。
我们注意到,fib(n-1) 和 fib(n-2) 实际上重复计算了很多结果,根本没必要,一个自然而然的想法就是把这些结果缓存起来,于是就有了下面的:
>>> def memoize(f): ... memo = {} ... def wrapper(x): ... if x not in memo: ... memo[x] = f(x) ... return memo[x] ... return wrapper ... >>> >>> def fib(n): ... if n in (0, 1): ... return n ... else: ... return fib(n-1) + fib(n-2) ... >>> fib = memoize(fib) >>> import timeit >>> timeit.timeit("fib(7)", "from __main__ import fib") 0.35535502433776855
还不够,Python 还有自己的解决办法,那就是用 decorator
[python]
def memoize(function):
memo = {}
def wrapper(args):
if args in memo:
return memo[args]
else:
rv = function(args)
memo[args] = rv
return rv
return wrapper
@memoize
def fib(n):
if n in (0, 1):
return n
return fib(n-1) + fib(n-2)
[/python]
为此,在 Python3 中还专门引入了一个 lru_cache。
这个东西的牛逼之处在于,有了它你可以放心地去写递归的代码,不用为了效率去把它改成迭代形式,你也知道,有些时候递归代码比迭代代码更容易理解。这样可读性有了,效率也提升了,不用修改函数的实现,直接让它从次方级降到了线性级。