Python dill 在 defaultdict 中一次性使用默认参数

Python's dill one-time use of default argument in a defaultdict

我正在努力解决我面临的这个奇怪问题。假设我们有 defaultdict 对象的以下实例:

import dill
from collections import defaultdict

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

让我们看看字典的样子:

defaultdict(<function <lambda> at 0x7f860c97be18>, {'a': 1.2, 'b': 3.5, 'c': 0.25})

在保存到 dill 对象之前,我想检查字典是否正常工作 - 正常并且添加了 'd'

print("d: ", dict_with_default['d'])

字典现在看起来像这样:

defaultdict(<function <lambda> at 0x7f860c97be18>, {'a': 1.2, 'b': 3.5, 'c': 0.25, 'd': 0.5})

由于我必须将字典保存到文件中(以便将其传输到不同的脚本)我将其保存到dill对象中:

with open('./pickles/simple_dict_dill.p', 'wb') as file:
    dill.dump(dict_with_default, file, protocol=dill.HIGHEST_PROTOCOL)

现在让我们将注意力转向我提到的不同脚本:

import dill

with open('./pickles/simple_dict_dill.p', 'rb') as file:
    simple_dict_dill = dill.load(file)

print("a:", simple_dict_dill['a'])
print("d:", simple_dict_dill['d'])
print("e:", simple_dict_dill['e']) # gives error

print("e:", simple_dict_dill['e']) 给出了以下错误,即使缺少密钥访问由 lambda: default_value 处理:

NameError: name 'default_value' is not defined

我以为 dill 可以序列化 lambda 函数,但事实证明它有问题。

我是 dill 的作者。这是由于 lambda 在全局字典外定义时如何序列化。您可以通过两种方式避免此错误:(1) 使用 recurse 设置,或 (2) 在全局字典中定义没有悬垂指针的 lambda。

例如:

>>> import dill
>>> dill.settings['recurse'] = True
>>> from collections import defaultdict
>>> a = 1
>>> b = 2
>>> f = lambda: a/b
>>> d = defaultdict(f)
>>> d['a'] = 3
>>> d['b'] = 4
>>> dill.dumps(d)
b'\x80\x03ccollections\ndefaultdict\nq\x00cdill._dill\n_create_function\nq\x01(cdill._dill\n_load_type\nq\x02X\x08\x00\x00\x00CodeTypeq\x03\x85q\x04Rq\x05(K\x00K\x00K\x00K\x02KCC\x08t\x00t\x01\x1b\x00S\x00q\x06N\x85q\x07X\x01\x00\x00\x00aq\x08X\x01\x00\x00\x00bq\t\x86q\n)X\x07\x00\x00\x00<stdin>q\x0bX\x08\x00\x00\x00<lambda>q\x0cK\x01C\x00q\r))tq\x0eRq\x0f}q\x10(h\x08K\x01h\tK\x02uh\x0cNN}q\x11tq\x12Rq\x13\x85q\x14Rq\x15(h\x08K\x03h\tK\x04u.'

然后我将生成的字符串剪切-N-粘贴(而不是写入文件......这是一回事)到新的 python 会话中。

$ python
Python 3.6.9 (default, Jul  6 2019, 02:58:03) 
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> d = dill.loads(b'\x80\x03ccollections\ndefaultdict\nq\x00cdill._dill\n_create_function\nq\x01(cdill._dill\n_load_type\nq\x02X\x08\x00\x00\x00CodeTypeq\x03\x85q\x04Rq\x05(K\x00K\x00K\x00K\x02KCC\x08t\x00t\x01\x1b\x00S\x00q\x06N\x85q\x07X\x01\x00\x00\x00aq\x08X\x01\x00\x00\x00bq\t\x86q\n)X\x07\x00\x00\x00<stdin>q\x0bX\x08\x00\x00\x00<lambda>q\x0cK\x01C\x00q\r))tq\x0eRq\x0f}q\x10(h\x08K\x01h\tK\x02uh\x0cNN}q\x11tq\x12Rq\x13\x85q\x14Rq\x15(h\x08K\x03h\tK\x04u.')
>>> d
defaultdict(<function <lambda> at 0x101d8d048>, {'a': 3, 'b': 4})
>>> d['a']
3
>>> d['c']
0.5
>>>

如果您不使用 recurse 设置,则必须使用:

>>> f = lambda: 1/2
>>> d = defaultdict(f)

当您创建 defaultdict 时。

当然,dill 应该能够更好地处理来自 lambda 的指针引用,但在某些情况下还不能。