Python记忆手动缓存

问题描述

我正在构建一个已记忆的Python Fibonacci函数的手动缓存版本,但我发现我没有在递归调用中将缓存作为参数传递。

但是,该功能在某种意义上仍比非存储版本要快得多。

当我将缓存作为函数参数添加时,该算法速度更快,但效果并不明显。

有人可以帮我理解为什么第一个版本完全有效,以及第二个版本是否更正确?

import time


def fib_cache(n,cache={}):
    if n in cache:
        return cache[n]
    if n == 0 or n == 1:
        return n
    result = fib_cache(n - 1) + fib_cache(n - 2)
    cache[n] = result
    return result


def fib_cache2(n,cache={}):
    if n in cache:
        return cache[n]
    if n == 0 or n == 1:
        return n
    result = fib_cache2(n - 1,cache) + fib_cache2(n - 2,cache)
    cache[n] = result
    return result

start = time.perf_counter()
fib_cache(30)
end = time.perf_counter()
print("Version 1. Seconds taken: {:.5f}".format(end - start))

start = time.perf_counter()
fib_cache2(30)
end = time.perf_counter()
print("Version 2. Seconds taken: {:.5f}".format(end - start))

解决方法

这是因为Python中的def仅执行一次,并且默认变量仅初始化一次。在引用类型的情况下,这可能导致错误/意外行为。一种解决方法是:

def fib_cache3(n,cache=None):
    if cache is None:
        cache = {}
    if n in cache:
        return cache[n]
    if n == 0 or n == 1:
        return n
    result = fib_cache3(n - 1,cache) + fib_cache3(n - 2,cache)
    cache[n] = result
    return result

此版本的优点在于它不依赖于引用类型的默认初始化,并且一旦执行该函数,便可以进行垃圾回收。