序列化和反序列化 lambda

问题描述 投票:0回答:3

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

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

因此,我的问题:

  • 如何从反序列化(反编组)代码对象重建函数?
  • 如何检测给定的 lambda 在没有相关闭包的情况下无法正常工作?
python lambda pickle
3个回答
22
投票

令人惊讶的是,检查 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 元组指定默认参数值。
可选的闭包元组提供自由变量的绑定。

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


4
投票

我不确定你到底想做什么,但你可以尝试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 还拥有 一些好的工具,可帮助您了解代码失败时导致酸洗失败的原因。


0
投票

我写了一个名为

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 相同。

区别在于简单和明确。

© www.soinside.com 2019 - 2024. All rights reserved.