我想在机器 A 上序列化并在机器 B 上反序列化 python lambda。有几个明显的问题:
因此,我的问题:
令人惊讶的是,检查 lambda 是否可以在没有关联闭包的情况下工作实际上相当容易。根据数据模型文档,您可以只检查
func_closure
属性:
>>> def get_lambdas(): ...酒吧= 42 ...返回(lambda:1,lambda:bar) ... >>> no_vars, vars = get_lambdas() >>> 打印 no_vars.func_closure 没有任何 >>> 打印 vars.func_closure (,) >>> 打印 vars.func_closure[0].cell_contents 42 >>> |
然后序列化 + 加载 lambda 就相当简单了:
>>> 导入元帅,类型 >>> 旧 = 拉姆达:42 >>> old_code_serialized = marshal.dumps(old.func_code) >>> new_code = marshal.loads(old_code_serialized) >>> new = types.FunctionType(new_code, globals()) >>> 新的() 42
值得一看的文档
FunctionType
:
函数(代码,全局变量[,名称[,argdefs[,闭包]]]) 从代码对象和字典创建函数对象。 可选的名称字符串会覆盖代码对象中的名称。 可选的 argdefs 元组指定默认参数值。 可选的闭包元组提供自由变量的绑定。
请注意,您还可以提供一个闭包...这意味着您甚至可以序列化旧函数的闭包,然后在另一端加载它:)
我不确定你到底想做什么,但你可以尝试dill。 Dill 可以序列化和反序列化 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 就可以神奇地使其工作,而无需对第 3 方代码进行猴子修补。或者,如果您希望将整个解释器会话作为“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:x\n'
您可以轻松地通过 ssh 将图像发送到另一台计算机,并从您离开的地方开始,只要 pickle 的版本兼容性以及有关 python 更改和安装的常见注意事项即可。如图所示,您还可以提取在上一个会话中定义的 lambda 的源。
Dill 还拥有 一些好的工具,可帮助您了解代码失败时导致酸洗失败的原因。
我写了一个名为
msgpickle
(pip install)的库。我写它的原因是因为我想要一个腌制器,我可以轻松控制什么可以腌制,什么不能腌制。
因此,虽然 pickling lambda 不安全,但您可以根据需要启用它并为任何类创建新的 pickler,并构建合理的序列化策略。
它的基础是
msgpack
,它使用它作为默认序列化器。
为了序列化 lambda,你可以这样做:
serializer = msgpickle.MsgPickle()
serializer.register(*msgpickle.cloud_function_serializer)
现在您的序列化器支持:
dat = serializer.dumps(lambda: 99)
fun = serializer.loads(dat)
assert fun() == 99
它的核心是function-packer,它只是使用了python的代码对象:
def cloud_func_pack(obj: Any) -> Any:
code_obj = obj.__code__
# this has a chance of working for future versions of Python
xmap = {"codestring": "code", "constants": "consts"}
code_arg_names = [
"co_" + xmap.get(param.name, param.name) for param in code_type_params.values()
]
def convert(value: Any) -> Any:
if isinstance(value, tuple):
return list(value)
return value
code_attributes = [convert(getattr(code_obj, attr)) for attr in code_arg_names]
return code_attributes
这意味着它在各个 python 版本中都是不安全的,但这与任何其他 pickler 相同。
区别在于简单和明确。