将 lambda 表达式保存到文件中

ale*_*s3d 5 python lambda python-3.x

我正在 Python 3 中实现进化策略算法。我创建了一个名为 individual 的类,它从文件(YAML 格式)中读取配置,如下所示:

num_of_genes: 3
pre_init_gene:
    gene1: 1.0
    gene2: 1.0
    gene3: 1.0
metrics:
    - metric1
    - metric2
obj_funcs:
    obj_fun1: 'lambda x,y: x+y'
    obj_fun2: 'lambda x: (x**3)/2'
Run Code Online (Sandbox Code Playgroud)

这个想法是个人将读取此文件以获取其配置。我知道我可以将 lambda 表达式保存为字符串,然后对其调用 eval。

然而,对于这个问题有没有更Pythonic的解决方案呢?我对 Python 中的 OO 感觉不太舒服,但我愿意接受建议。

Ant*_*hon 4

我会坚持Python 的禅宗,特别是“显式优于隐式”和“可读性很重要”。

因此,将函数作为定义 lambda 的可读字符串是一个好主意,尽管出于安全原因,调用evallambda 的加载字符串表示形式可能不是个好主意。这又取决于谁有权修改文件以及他们在哪个系统上运行。

一般来说,如果有人可以(非无意地)注入某些东西,从而导致递归删除系统上的所有文件,如果他们拥有无论如何都可以这样做的登录访问权限,那么您不应该太在意。但是,如果软件在远程系统上运行,并且这些文件可以通过某些 Web 界面进行编辑,或者如果文件更改可以由使用这些文件的人以外的其他人进行,那么您应该考虑这一点。

如果 lambda 来自固定集合,您可以仅使用它们的字符串表示形式作为查找:

lambdas = {}
for l in [
   'lambda x,y: x+y',
   'lambda x: (x**3)/2',
   # some more
]:
   lambdas[l] = eval(l)
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用从配置 YAML 加载的字符串来获取实际的 lambda,并且该字符串不能被篡改,因为它必须与您提供的可用 lambda 集相匹配。当然,您可以从只有您可以更改的文件中加载实际的 lambda 字符串,而不是在源代码中对它们进行硬编码。

在我看来,这比转储实际的 lambda 更明确,导致 YAML 看起来像这样:

!!python/name:__main__.%3Clambda%3E
Run Code Online (Sandbox Code Playgroud)

,无论如何都需要不安全地加载 YAML 文档。

如果您需要比使用预定义的 lambda 更灵活,但又不想使用 的不安全性eval,那么另一种可能性是使用 Python 的AST 模块。该模块允许安全地评估一元和二元运算符,但可以扩展为仅处理您希望在 lambda 中允许的那些函数(例如某些数学函数)。我在我的 Python 对象表示法模块 ( PON )中做了类似的扩展, 为 AST 评估的输入添加了日期时间和删除功能。


另外,在我看来,你应该改进你的 YAML。不要使用 gene1,gene2作为映射中的键,而是使用序列并标记项目:

pre_init_gene:
    - !Gene 1.0
    - !Gene 1.0
    - !Gene 1.0
Run Code Online (Sandbox Code Playgroud)

或者,也可以标记序列:

pre_init_gene: !Genes
    - 1.0
    - 1.0
    - 1.0
Run Code Online (Sandbox Code Playgroud)

你的 lambda 有同样的“问题”,我会做类似的事情:

obj_funcs:
   - !Lambda 'x, y: x+y'
   - !Lambda 'x: (x**3)/2'
Run Code Online (Sandbox Code Playgroud)

from_yaml classmethod其中实现标签的对象!Lambda透明地执行 eval 或 AST 评估。