如何避免每次重新加载Python模块时进行计算
我有一个Python模块,它使用了一个非常大的全局字典变量。目前,我把计算代码放在模块的顶部,每次第一次导入或重新加载这个模块都需要超过一分钟,这实在是太慢了。我想知道怎么才能把计算结果保存起来,这样下次导入或重新加载的时候就不需要再计算了。我试过用cPickle,但从文件中加载这个字典变量(1.3M)所花的时间大约和计算的时间差不多。
为了更详细地说明我的问题,
FD = FreqDist(word for word in brown.words()) # this line of code takes 1 min
13 个回答
我猜你是把字典的内容直接粘贴到代码里了,这样可能会导致加载时变得很慢吧?我不太清楚怎么解决这个问题,不过你可以考虑在导入的时候不立即创建这个字典……可以等到第一次真正用到它的时候再去创建。
在第一次使用时计算你的全局变量。
class Proxy:
@property
def global_name(self):
# calculate your global var here, enable cache if needed
...
_proxy_object = Proxy()
GLOBAL_NAME = _proxy_object.global_name
或者更好的是,通过特殊的数据对象来获取必要的数据。
class Data:
GLOBAL_NAME = property(...)
data = Data()
举个例子:
from some_module import data
print(data.GLOBAL_NAME)
可以查看 Django 设置。
为了澄清一下:模块内部的代码并不是每次导入模块时都会执行,而是只会执行一次。之后的导入会找到已经创建好的模块,而不是重新创建一个。你可以查看 sys.modules 来看到缓存的模块列表。
不过,如果你遇到的问题是程序运行后第一次导入所需的时间,那你可能需要用其他方法,而不是用 Python 字典。最好的办法可能是使用一种磁盘存储的形式,比如 sqlite 数据库,或者 dbm 模块中的某一个。
如果你想对接口做最小的改动,shelve 模块可能是你最好的选择——它在 dbm 模块之间提供了一个比较透明的接口,让它们表现得像一个普通的 Python 字典,可以存储任何可以被序列化的值。这里有个例子:
# Create dict with a million items:
import shelve
d = shelve.open('path/to/my_persistant_dict')
d.update(('key%d' % x, x) for x in xrange(1000000))
d.close()
然后在下一个进程中使用它。查找时应该不会有太大的延迟,因为只会对请求的键在磁盘上进行查找,所以并不需要把所有内容都加载到内存中:
>>> d = shelve.open('path/to/my_persistant_dict')
>>> print d['key99999']
99999
它比真正的字典稍慢,而且如果你做一些需要所有键的操作(比如尝试打印它),加载时间会很长,但这可能会解决你的问题。