我有一个python模块,它使用了一个巨大的字典全局变量,目前我将计算代码放在顶部,每次第一次导入或重新加载模块需要超过一分钟,这是完全不可接受的。如何在某处保存计算结果,以便下次导入/重新加载不必计算它?我尝试了cPickle,但是从文件(1.3M)加载字典变量与计算大致相同。
要提供有关我的问题的更多信息,
FD = FreqDist(word for word in brown.words()) # this line of code takes 1 min
答案 0 :(得分:17)
只是为了澄清:每次导入模块时,模块正文中的代码都是而不是执行 - 它只运行一次,之后将来导入找到已创建的模块,而不是重新创造它。查看sys.modules以查看缓存模块列表。
但是,如果您的问题是程序运行后第一次导入所需的时间,您可能需要使用除python dict之外的其他方法。可能最好使用磁盘形式,例如sqlite数据库,其中一个dbm模块。
对于界面中的最小变化,搁置模块可能是您的最佳选择 - 这会在dbm模块之间建立一个非常透明的接口,使它们像任意python dict一样,允许存储任何可选值。这是一个例子:
# 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
它比真正的字典慢一点,如果你做了需要所有键的事情(例如尝试打印它), 仍需要很长时间才能加载,但可能会解决你的问题。
答案 1 :(得分:3)
首次使用时计算全局变量。
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
或者更好的是,通过特殊数据对象访问necessery数据。
class Data:
GLOBAL_NAME = property(...)
data = Data()
示例:
from some_module import data
print(data.GLOBAL_NAME)
请参阅Django settings。
答案 2 :(得分:2)
我假设你已经将dict文字粘贴到了源代码中,这就是花了一分钟的时间?我不知道怎么解决这个问题,但是你可以避免在 import 上实例化这个dict ...你可以在它第一次实际使用时懒惰地实例化它。
答案 3 :(得分:2)
您可以尝试使用marshal模块而不是c?Pickle模块;它可能会更快。 python使用此模块以二进制格式存储值。请特别注意以下段落,看看元帅是否符合您的需求:
并非所有Python对象类型都受支持;通常,此模块只能编写和读取其值独立于Python的特定调用的对象。支持以下类型:无,整数,长整数,浮点数,字符串,Unicode对象,元组,列表,集,字典和代码对象,应该理解元组,列表和字典仅支持因为其中包含的价值本身得到支持;并且不应该写入递归列表和字典(它们将导致无限循环)。
为了安全起见,在解组dict之前,请确保解组dict的Python版本与执行编组的Python版本相同,因为无法保证向后兼容性。
答案 4 :(得分:2)
答案 5 :(得分:2)
shelve
变得非常慢。我一直非常成功地使用redis,并围绕它写了一个FreqDist wrapper。它非常快,可以同时访问。
答案 6 :(得分:1)
您可以使用shelve将数据存储在光盘上,而不是将整个数据加载到内存中。因此启动时间会非常快,但权衡时间会较慢。
Shelve也会腌制dict值,但会在启动时为所有项目执行(un)pickle,但仅限于每个项目本身的访问时间。
答案 7 :(得分:1)
有助于加快进口的一些事情:
话虽如此,我同意您在第一次导入模块后不应该遇到导入模块的任何延迟。以下是其他一些一般性想法:
话虽如此,如果没有更多的背景,给你提供任何具体的建议有点困难。更具体地说,你在哪里导入它?什么是计算?
答案 8 :(得分:1)
将计算密集型部分视为一个单独的模块。那么至少在重装时,你不必等待。
尝试使用协议2转储数据结构。要尝试的命令是cPickle.dump(FD, protocol=2)
。来自cPickle.Pickler
的文档字符串:
Protocol 0 is the only protocol that can be written to a file opened in text mode and read back successfully. When using a protocol higher than 0, make sure the file is opened in binary mode, both when pickling and unpickling.
答案 9 :(得分:1)
我正在经历同样的问题...... 搁置,数据库等......对于这类问题来说都太慢了。您需要点击一次,将其插入像Redis这样的内存键/ val存储中。它只会存在于内存中(警告它可能会占用大量内存,因此您可能需要一个专用的盒子)。你永远不必重新加载它,你只需要在内存中寻找密钥
r = Redis()
r.set(key, word)
word = r.get(key)
答案 10 :(得分:0)
扩展延迟计算的想法,为什么不把dict变成一个必要时提供(和缓存)元素的类?
您也可以使用psyco加速整体执行......
答案 11 :(得分:0)
OR 您可以使用数据库存储值吗?查看SQLObject,这样可以很容易地将东西存储到数据库中。
答案 12 :(得分:0)
对于这个问题,还有另一个非常明显的解决方案。重新加载代码时,原始范围仍然可用。
所以......做这样的事情会确保这段代码只执行一次。
try:
FD
except NameError:
FD = FreqDist(word for word in brown.words())