我有一个看起来像这样的defaultdict:
dict1 = defaultdict(lambda: defaultdict(int))
问题是,我不能用cPickle来腌制它。我在这里找到的解决方案之一是使用模块级函数而不是lambda。我的问题是,什么是模块级功能?如何将字典与cPickle一起使用?
答案 0 :(得分:50)
模块级函数是在模块级别定义的函数,这意味着它不是类的实例方法,它不嵌套在另一个函数中,它是一个带有名称的“真实”函数,而不是一个lambda函数。
所以,为了挑选你的defaultdict
,用模块级函数而不是lambda函数创建它:
def dd():
return defaultdict(int)
dict1 = defaultdict(dd) # dd is a module-level function
比你可以腌制它
tmp = pickle.dumps(dict1) # no exception
new = pickle.loads(tmp)
答案 1 :(得分:15)
Pickle希望存储所有实例属性,而defaultdict
实例存储对default
可调用的引用。 Pickle会对每个实例属性进行递归。
Pickle无法处理lambdas; pickle只处理数据,而不是代码,lambdas包含代码。函数可以进行pickle,但只有在函数可以导入时才像类定义一样。可以导入在模块级别定义的函数。在这种情况下,Pickle只存储一个字符串,这是要导入的函数的完整“路径”,并在再次打开时引用。
答案 2 :(得分:12)
然而,您可以使用partial
来完成此任务:
>>> from collections import defaultdict
>>> from functools import partial
>>> pickle.loads(pickle.dumps(defaultdict(partial(defaultdict, int))))
defaultdict(<functools.partial object at 0x94dd16c>, {})
答案 3 :(得分:7)
要做到这一点,只需编写您想要编写的代码。我使用dill,它可以序列化lambdas和defaultdicts。 Dill可以在python中序列化几乎任何东西。
>>> import dill
>>> from collections import defaultdict
>>>
>>> dict1 = defaultdict(lambda: defaultdict(int))
>>> pdict1 = dill.dumps(dict1)
>>> _dict1 = dill.loads(pdict1)
>>> _dict1
defaultdict(<function <lambda> at 0x10b31b398>, {})
答案 4 :(得分:2)
如果您不关心保留defaultdict类型,请将其转换为:
fname = "file.pkl"
for value in nested_default_dict:
nested_default_dict[value] = dict(nested_default_dict[value])
my_dict = dict(nested_default_dict)
with open(fname, "wb") as f:
pickle.dump(my_dict, f) # Now this will work
我认为这是一个很好的选择,因为当你在腌制时,对象可能是它的最终形式...而且,如果确实需要再次使用defaultdict类型,你可以简单地转换回来之后unpickle:
for value in my_dict:
my_dict[value] = defaultdict(type, my_dict[value])
nested_default_dict = defaultdict(type, my_dict)
答案 5 :(得分:1)
我目前正在做类似问题的事情,但是,我正在使用defaultdict的子类,它有一个成员函数,用作default_factory。为了让我的代码正常工作(我需要在运行时定义函数),我只是添加了一些代码来准备酸洗对象。
而不是:
...
pickle.dump(dict, file)
...
我用这个:
....
factory = dict.default_factory
dict.default_factory = None
pickle.dump(dict, file)
dict.default_factory = factory
...
这不是我使用的确切代码,因为我的树是一个对象,它会在请求索引时创建与树类型相同的实例(所以我使用递归成员函数来执行pre / post pickle操作),但是这种模式也回答了这个问题。
答案 6 :(得分:1)
dict1 = defaultdict(lambda: defaultdict(int))
cPickle.dump(dict(dict1), file_handle)
为我工作
答案 7 :(得分:1)
在这种情况下,该解决方案仍然可以作为一种工具使用,并且实际上比lambda
(或等效的def
-ed版本)启动功能更有效:
dict1 = defaultdict(defaultdict(int).copy)
这只是制作一个模板defaultdict(int)
,并将其copy
方法绑定为外部defaultdict
的默认工厂。那里的所有东西都是可拾取的,并且在CPython(其中defaultdict
是用C实现的内置类型)上,它比调用任何用户定义的函数来完成相同的工作更为有效。无需额外的进口,包装等。
答案 8 :(得分:0)
用普通函数实现匿名lambda函数对我来说很有效。正如Mike所指出的,Pickle无法处理lambda。 pickle只处理数据。因此,将我的方法从以下位置转换:
dict_ = defaultdict(lambda: default_value)
收件人:
default_():
return default_value
然后按如下所示创建默认字典对我有用:
dict_ = defaultdict(default_)
答案 9 :(得分:0)
这是一个用于任意嵌套深度的任意基本 defaultdict 的函数。
def wrap_defaultdict(instance, times):
"""Wrap an instance an arbitrary number of `times` to create nested defaultdict.
Parameters
----------
instance - e.g., list, dict, int, collections.Counter
times - the number of nested keys above `instance`; if `times=3` dd[one][two][three] = instance
Notes
-----
using `x.copy` allows pickling (loading to ipyparallel cluster or pkldump)
- thanks https://stackoverflow.com/questions/16439301/cant-pickle-defaultdict
"""
from collections import defaultdict
def _dd(x):
return defaultdict(x.copy)
dd = defaultdict(instance)
for i in range(times-1):
dd = _dd(dd)
return dd