Python可以在内存中创建大的dicts但不能从文件加载它们

时间:2017-12-19 16:50:50

标签: python dictionary

我的工作涉及使用python import { NavigationActions } from 'react-navigation' const resetAction = NavigationActions.reset({ index: 0, actions: [ NavigationActions.navigate({ routeName: 'Profile'}) ] }) this.props.navigation.dispatch(resetAction) 创建查找表。构建所有这些dicts需要大约20分钟,并使用大约2GB的RAM。我试图通过将所有这些dicts写入defaultdict(set)文件,然后通过导入将它们重新加载来节省时间。

我正在使用.py编写文件以删除theFile.write("idToName = {}\n".format(dict(idToName)))类的设置部分。该文件大约500MB,并且dicts都可以正常工作。但是,当我尝试将文件导入其中时,填充我的内存并锁定所有内容。什么会导致ram使用的这种差异?

3 个答案:

答案 0 :(得分:3)

我猜你正在抓住计算机内存的极限。当您将一个巨大的字典写入.py文件时,您当然也有一个巨大的.py文件。如果您现在尝试导入它,那么Python解释器需要做的不仅仅是在内存中保存字典。它需要打开源文件,读取,编译,将其字节码表示(编译结果)写入.pyc文件,然后执行它,最后再次在内存中创建字典。所有这些意味着同时在内存中保存多种格式的数据。

我认为你的方法存在缺陷。不应通过编写.py文件来存储数据。使用称为序列化的技术(有时也称为编组)存储它会好得多,而对于Python,也称为pickling,因为它可以是完成标准模块pickle(或cPickle以获得更好的性能)。

创建值后,您应该使用pickle模块存储(字典)。然后当您再次需要它们时,再次从pickle商店文件中读取

import pickle
value = create_my_huge_dictionary()
with open('my_dictionary.pickle', 'w') as store_file:
    pickle.store(store_file, value)

然后,可能在另一个脚本中:

import pickle
with open('my_dictionary.pickle') as store_file:
    value = pickle.load(store_file)

仍然是关于您要删除的defaultdict的主题。上面提到的方法不会这样做。将defaultdict存储在pickle文件中并从那里再次读取该值将重新创建defaultdict,而不是dict

我的建议是与之共存,因为拥有defaultdict而不是dict可能会让您感到痛苦。但是,万一这是不可行的,你应该考虑首先不要使用defaultdict。您可以使用此模式使用普通dict来实现其功能:

d = {}
d.setdefault('a', {}).setdefault('b', 4)
# d will now be {'a': {'b': 4}}

当然,您可以在腌制之前或之后尝试将<{1}} 转换为defaultdict。您只需说明dict即可。但这肯定意味着在记忆中将它短暂两次。也许你的RAM并没有受到影响,你再次陷入困境。

如果你使用d = dict(d)来存储你的字典(也许它很简单),那么一旦它json的信息也会在序列化之后消失。

答案 1 :(得分:1)

你没有提到你正在使用的是什么版本的Python,但是python可能是极限导入一个大的dict。如果dict在创建时占用2GB,则需要更多才能导入它。

您没有使用合适的工具来完成工作。您应该使用pickle模块将数据序列化为文件或使用数据库。

或者你可以做这样的事情并制作一个懒惰的加载字典。这样你就可以在实际需要的时候获得物品。 PSUEDO CODE。

首先从你的词典中制作一个书架文件。

def make_dict_shelf(my_dict, path):
        """
        Make a shelf for containing the items; this is typically only done when the dict is generated
        """
        s = shelve.open(path, "n", protocol=pickle.HIGHEST_PROTOCOL)
        for key, value in my_dict.items():
            s[key] = value
        s.close()

使用python shelf延迟加载dict。

def load_dict_shelf(my_lazy_dict, path):
        """
        Load the items from a python shelve via LazyLoadingDict as needed.
        """

class LazyLoadingDict(collections.MutableMapping):
            """
            Special dict that only loads items as they are accessed. If a item is accessed it gets copied from the
            shelve to the cache dict. All user items are saved in the cache ONLY. Saving data back to the shelf
            is currently NOT supported
            """
            def __init__(self, source):
                self.source = source  # python shelf
                self.cache = {}  # internal dict

            def __getitem__(self, key):
                # try to get the item from the cache, if it isn't there get it from the shelf
                try:
                    return self.cache[key]
                except KeyError:
                    node = self.cache[key] = self.source[key.to_string()]
                    return node

            def __setitem__(self, key, value):
                # add a new item to the cache; if this item is in the shelf it is not updated
                self.cache[key] = value

            def __contains__(self, key):
                return key in self.cache or key.to_string() in self.source

            def __delitem__(self, key):
                # only deleting items from the cache is allowed
                del self.cache[key]

            def __iter__(self):
                # only the cache can be iterated over
                return iter(self.cache.keys())

            def __len__(self):
                # only returns the length of items in the cache, not unaccessed items in the shelf
                return len(self.cache)

        my_lazy_dict._items = LazyLoadingDict(shelve.open(path, "r"))

答案 2 :(得分:1)

我找到了满足我所有需求的解决方案。正确答案是因为他们确实回答了我的文件无法加载的原因。

首先,我使用shelve模块将数据保存到文件中,并使用变量名作为搁置字典的键。

然后,为了加载它并像我一样使用数据,我打开了搁架,创建了一个基于object的空类,然后是魔术:

nb = MyClass()
for k,v in shelv.items():
    setattr(nb,k,v)

我可以像往常一样在任何地方使用nb.idToName['1234']

数据文件只有90MB,并在30秒内加载。更容忍。

感谢所有帮助推动我的人!