【问题标题】:How can I load Python lambda expressions from YAML files using ruamel.yaml?如何使用 ruamel.yaml 从 YAML 文件加载 Python lambda 表达式?
【发布时间】:2019-12-08 08:29:35
【问题描述】:

我正在尝试使用ruamel.yaml 序列化和反序列化包含 lambda 表达式的对象。如示例中所示,这会产生ConstructorError。如何做到这一点?

import sys
import ruamel.yaml

yaml = ruamel.yaml.YAML(typ='unsafe')
yaml.allow_unicode = True
yaml.default_flow_style = False

foo = lambda x: x * 2
yaml.dump({'foo': foo}, sys.stdout)
# foo: !!python/name:__main__.%3Clambda%3E

yaml.load('foo: !!python/name:__main__.%3Clambda%3E')
# ConstructorError: while constructing a Python object
# cannot find '<lambda>' in the module '__main__'
#   in "<unicode string>", line 1, column 6

【问题讨论】:

    标签: python-3.x serialization lambda yaml ruamel.yaml


    【解决方案1】:

    那是行不通的。 ruamel.yaml 转储函数(或方法),方法是通过引用源代码中的函数名称(即它不尝试存储实际代码)来引用这些函数。

    您的lambda 是一个匿名函数,因此没有可以正确检索到的名称。同样,Python 的pickle 不支持lambda

    我不确定尝试转储lambda 是否应该是错误,或者是否应该发出警告。

    简单的解决方案是将您的lambda(s) 变成命名函数。或者,您可能能够获取 lambda 的实际代码或 AST 并存储和检索它,但这将是更多的工作并且可能不可移植,具体取决于您存储的内容。

    【讨论】:

    • 感谢您的回复!存储实际代码将提供更具可读性的 YAML 文件,但如果不假设全局命名空间(例如导入的包),似乎几乎不可能反序列化。只要 Python 代码不变,我就可以存储一个反序列化的引用。是否可以将 lambda 序列化为 !!python/name:module.scope.lambda_n,其中 n 指的是引用此范围内的哪个 lambda?
    • 是的,会有更多假设,但您仍然可以存储模块名称(与类和函数一样),加载模块(即解析全局命名空间的所有合理假设)。 IIRC lambdas 有一个唯一的 id,它不基于它们的(字符串)内容,但即使在同一 Python 版本上运行时也会有所不同。所以!&lt;python/lambda:__main__.x: x * 2&gt; 似乎是更好的选择。我会考虑的另一件事是查看cloudpickledill 他们如何选择 lambdas(以及这些软件包的限制)
    • 再次感谢。是否可以使用 dill 的 lambda 序列化和反序列化作为 ruamel.yaml 的自定义表示和构造函数?
    • 是的,我认为这是可能的。您需要在表示器中区分“普通”函数和 lambda,然后将 lambda 腌制为二进制文件(或者更好,当 dill 支持协议 = 1 时,字符串表示)并将其作为参数写入标签 `! !python/lambda'' (而不是像我之前的评论那样使 lambda 内容成为标签的一部分)。来自 pickle 的二进制表示需要更多的工作来编码为 ASCII/UTF-8(例如 base64 编码)。你需要反向才能再次加载它。
    猜你喜欢
    • 1970-01-01
    • 2016-11-10
    • 1970-01-01
    • 1970-01-01
    • 2021-08-30
    • 1970-01-01
    • 2018-11-27
    • 2019-05-16
    • 2021-06-03
    相关资源
    最近更新 更多