序列化和反序列化lambda

时间:2012-08-09 07:02:45

标签: python lambda pickle

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

  • pickle模块不会序列化或反序列化代码。它只序列化类/方法/函数的名称
  • 我在谷歌找到的一些答案建议使用低级编组模块来序列化lambda的func_code属性,但是他们无法描述如何从反序列化的代码对象重构函数对象
  • marhshal(l.func_code)不会序列化与lambda关联的闭包,导致检测到给定lambda何时真正需要闭包并警告用户他正在尝试序列化使用闭包的lambda < / LI>

因此,我的问题:

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

2 个答案:

答案 0 :(得分:19)

令人惊讶的是,检查lambda是否在没有关联闭包的情况下工作实际上相当容易。根据{{​​3}},您只需查看func_closure属性:

即可
>>> def get_lambdas():
...     bar = 42
...     return (lambda: 1, lambda: bar)
...
>>> no_vars, vars = get_lambdas()
>>> print no_vars.func_closure
None
>>> print vars.func_closure
(<cell at 0x1020d3d70: int object at 0x7fc150413708>,)
>>> print vars.func_closure[0].cell_contents
42
>>>

然后序列化+加载lambda非常简单:

>>> import marshal, types
>>> old = lambda: 42
>>> old_code_serialized = marshal.dumps(old.func_code)
>>> new_code = marshal.loads(old_code_serialized)
>>> new = types.FunctionType(new_code, globals())
>>> new()
42

值得一看FunctionType的文档:

function(code, globals[, name[, argdefs[, closure]]])

Create a function object from a code object and a dictionary.
The optional name string overrides the name from the code object.
The optional argdefs tuple specifies the default argument values.
The optional closure tuple supplies the bindings for free variables.

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

答案 1 :(得分:4)

我不确定你想做什么,但你可以试试dill。 Dill可以序列化和反序列化lambdas,我相信也适用于闭包内的lambdas。 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就可以神奇地使它无需工作monkeypatching第三方代码。或者,如果您希望整个解释器会话通过线路发送为“python图像”,莳萝也可以这样做。

>>> # 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:x\n'

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

Dill还有some good tools帮助您了解代码失败时导致酸洗失败的原因。