使用dill序列化含lambda默认值的defaultdict时遇NameError问题
问题原因分析
你遇到的问题本质是lambda函数的闭包变量没有被dill正确序列化到目标环境。你的lambda: default_value依赖了外部定义的default_value变量,dill在序列化这个lambda时,只是保存了lambda的代码逻辑,但并没有把default_value这个变量的内容一起打包到序列化文件里。当你在另一个脚本加载后访问新键时,lambda试图去当前环境找default_value,但这个变量在新脚本里根本没定义,所以就抛出了NameError。
解决方案
这里有几种可行的解决办法,按简单程度排序:
1. 直接把默认值内联到lambda中
最直接的方式是避免lambda依赖外部变量,把default_value的计算结果直接写进lambda里:
# 序列化脚本中修改这一行 dict_with_default = defaultdict(lambda: 0.5) # 因为a/b=1/2=0.5
这样lambda不再依赖任何外部变量,序列化和反序列化后,访问新键时能直接返回0.5,不会有变量找不到的问题。
2. 使用functools.partial绑定默认值
如果你的默认值是动态计算的,不想硬编码,可以用functools.partial来创建一个绑定了默认值的函数,它比lambda更容易被序列化且不依赖外部变量:
import dill from collections import defaultdict from functools import partial a = 1 b = 2 default_value = a/b # 用partial创建一个返回default_value的函数 default_func = partial(lambda x: x, default_value) dict_with_default = defaultdict(default_func) # 后续的序列化代码不变 dict_with_default['a'] = 1.2 dict_with_default['b'] = 3.5 dict_with_default['c'] = 0.25 with open('./pickles/simple_dict_dill.p', 'wb') as file: dill.dump(dict_with_default, file, protocol=dill.HIGHEST_PROTOCOL)
这样default_func已经把default_value的值绑定在了函数内部,反序列化后不需要外部环境有这个变量就能正常工作。
3. 让dill递归序列化闭包变量
dill其实支持序列化闭包变量,但需要开启递归序列化的设置。你可以在序列化前添加这一行:
dill.settings['recurse'] = True
完整的序列化代码:
import dill from collections import defaultdict dill.settings['recurse'] = True # 开启递归序列化闭包 a = 1 b = 2 default_value = a/b dict_with_default = defaultdict(lambda: default_value) dict_with_default['a'] = 1.2 dict_with_default['b'] = 3.5 dict_with_default['c'] = 0.25 with open('./pickles/simple_dict_dill.p', 'wb') as file: dill.dump(dict_with_default, file, protocol=dill.HIGHEST_PROTOCOL)
这样dill会把lambda依赖的default_value也一起序列化到文件里,反序列化后就能正常访问新键了。
验证测试
在加载脚本中执行以下代码,三种方案都能正常输出:
import dill with open('./pickles/simple_dict_dill.p', 'rb') as file: simple_dict_dill = dill.load(file) print("a:", simple_dict_dill['a']) # 输出1.2 print("d:", simple_dict_dill['d']) # 输出0.5 print("e:", simple_dict_dill['e']) # 输出0.5,不再报错
内容的提问来源于stack exchange,提问作者balkon16
相关产品推荐
相关产品推荐

