序列化和反序列化 lambda



我想在机器 A 上序列化并在机器 B 上反序列化 python lambda。这有几个明显的问题:

  • pickle 模块不序列化或反序列化代码。它只序列化类/方法/函数的名称
  • 我在谷歌上找到的一些答案建议使用低级封送模块来序列化lambda的func_code属性,但它们未能描述如何从反序列化的代码对象重建函数对象
  • Marhshal(l.func_code) 不会序列化与 lambda 关联的闭包,这会导致检测给定 lambda 何时确实需要闭包并警告用户他正在尝试序列化使用闭包的 lambda
  • 的问题。

因此,我的问题:

  • 如何从反序列化(解定)代码对象重建函数?
  • 如果没有相关的闭包,如何检测给定的 lambda 将无法正常工作?

令人惊讶的是,检查 lambda 是否可以在没有其相关闭包的情况下工作实际上相当容易。根据数据模型文档,您只需检查 func_closure 属性:

>>> def get_lambdas():...    柱 = 42...    返回 (λ: 1, λ: bar)...>>> no_vars, 变量 = get_lambdas()>>>打印no_vars.func_closure没有>>>打印vars.func_closure(<0x1020d3d70 处的单元格:0x7fc150413708>处的整数对象,)>>>打印vars.func_closure[0].cell_contents42>>>

然后序列化 + 加载 lambda 相当简单:

>>> 导入元帅,类型>>>旧 = λ:42>>> old_code_serialized = 元帅转储(old.func_code)>>> new_code = 元帅负载(old_code_serialized)>>>新的 = 类型。函数类型(new_code, 全局())>>> 新()42

值得一看的是FunctionType的文档:

function(code, globals[, name[, argdefs[, closure]]])从代码对象和字典创建函数对象。可选名称字符串将覆盖代码对象中的名称。可选的 argdefs 元组指定默认参数值。可选的闭包元组为自由变量提供绑定。

请注意,您还可以提供闭包...这意味着您甚至可以序列化旧函数的闭包,然后将其加载到另一端:)

我不确定你想做什么,但你可以试试莳萝。 莳萝可以序列化和反序列化 lambda,我相信也适用于闭包内的 lambda。 pickle API 是其 API 的子集。 要使用它,只需"进口莳萝作为泡菜",然后开始您的业务腌制工作。

>>> import dill
>>> testme = lambda x: lambda y:x
>>> _testme = dill.loads(dill.dumps(testme))
>>> testme
<function <lambda> at 0x1d92530>
>>> _testme
<function <lambda> at 0x1d924f0>
>>> 
>>> def complicated(a,b):
...   def nested(x):
...     return testme(x)(a) * b
...   return nested
... 
>>> _complicated = dill.loads(dill.dumps(complicated))
>>> complicated 
<function complicated at 0x1d925b0>
>>> _complicated
<function complicated at 0x1d92570>

Dill将其类型注册到pickle注册表中,因此,如果您有一些使用pickle的黑盒代码并且您无法真正编辑它,那么仅导入Dill就可以神奇地使其工作而无需猴子修补第三方代码。或者,如果您希望整个解释器会话作为"python图像"通过网络发送,Dill也可以这样做。

>>> # continuing from above
>>> dill.dump_session('foobar.pkl')
>>>
>>> ^D
dude@sakurai>$ python
Python 2.7.5 (default, Sep 30 2013, 20:15:49) 
[GCC 4.2.1 (Apple Inc. build 5566)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> dill.load_session('foobar.pkl')
>>> testme(4)
<function <lambda> at 0x1d924b0>
>>> testme(4)(5)
4
>>> dill.source.getsource(testme)
'testme = lambda x: lambda y:xn'

您可以轻松地通过ssh将映像发送到另一台计算机,并从上次中断的地方开始,只要有pickle的版本兼容性以及有关python更改和安装内容的通常警告。 如图所示,您还可以提取在上一个会话中定义的 lambda 的源。

Dill还有一些很好的工具,可以帮助您了解在代码失败时导致酸洗失败的原因。

最新更新