对Python函数进行哈希以在函数修改时重新生成输出
我有一个Python函数,它的结果是确定的,也就是说每次运行都会得到相同的结果。不过,这个函数运行起来很慢,而且输出的结果也很大:
def time_consuming_function():
# lots_of_computing_time to come up with the_result
return the_result
我会不时修改这个time_consuming_function
,但我希望在它没有变化的时候,避免再次运行它。这里的意思是,time_consuming_function
只依赖于一些不会改变的函数,比如Python库里的函数,而不是我自己代码中的那些可能会改动的部分。对我来说,比较好的解决办法是缓存这个函数的输出结果,同时也缓存一个“哈希值”来表示这个函数。如果哈希值发生了变化,说明这个函数被修改过了,那我们就需要重新生成输出结果。
这样做可行吗?还是说太荒唐了?
更新:根据大家的回答,我发现我想做的其实是对time_consuming_function
进行“备忘录化”,不过我想要的不仅仅是对传入的参数进行处理,而是要考虑到这个函数本身可能会发生变化。
5 个回答
第一部分是关于如何把你的查找表进行记忆化和序列化。这部分应该比较简单,可以用一些Python的序列化库来实现。第二部分是,当你的源代码发生变化时,你想要删除已经序列化的查找表。也许这个问题被想得太复杂了。一般来说,当你修改代码时,应该会把它提交到某个地方吧?为什么不在提交的过程中加一个钩子,自动删除你的序列化表呢?或者如果这不是研究数据,而是生产环境中的数据,那就把这个过程放到你的发布流程中:如果你的文件的版本号发生了变化(把这个功能放在一个单独的文件里),那么你的发布脚本就会删除序列化的查找表。
与其把函数放在一个字符串里,不如把这个函数放在一个单独的文件里。比如可以叫它 time_consuming.py。它的内容大概是这样的:
def time_consuming_method():
# your existing method here
# Is the cached data older than this file?
if (not os.path.exists(data_file_name)
or os.stat(data_file_name).st_mtime < os.stat(__file__).st_mtime):
data = time_consuming_method()
save_data(data_file_name, data)
else:
data = load_data(data_file_name)
# redefine method
def time_consuming_method():
return data
在测试这个功能是否能正常工作的时候,我会把那些慢的部分注释掉。先写一个简单的函数,让它只返回0,确保所有的保存和加载功能都能正常工作,然后再把那些慢的部分加回去。
如果我理解你的问题,我会这样处理。虽然这个方法有点不太正统,但我觉得它比这里其他的解决方案更可靠,更直接。
import inspect
import functools
import json
def memoize_zeroadic_function_to_disk(memo_filename):
def decorator(f):
try:
with open(memo_filename, 'r') as fp:
cache = json.load(fp)
except IOError:
# file doesn't exist yet
cache = {}
source = inspect.getsource(f)
@functools.wraps(f)
def wrapper():
if source not in cache:
cache[source] = f()
with open(memo_filename, 'w') as fp:
json.dump(cache, fp)
return cache[source]
return wrapper
return decorator
@memoize_zeroadic_function_to_disk(...SOME PATH HERE...)
def time_consuming_function():
# lots_of_computing_time to come up with the_result
return the_result