Python 如何执行存储为字符串的布尔逻辑,最好不使用eval()?

Python 如何执行存储为字符串的布尔逻辑,最好不使用eval()?,python,yaml,eval,pyyaml,Python,Yaml,Eval,Pyyaml,我有以下YAML文件,其中包含与文件名匹配的解析逻辑: rules\u container=“” 文件名: 规则_1:{module:str,method:startswith,args:[s_]} 规则2:{module:str,method:endswith,args:[.log]} 逻辑:规则1和规则2 """ 我加载它并得到以下映射: >>导入yaml >>>导入pprint >>d_yml=yaml.安全装载(规则容器) >>>pprint.pprint(d_yml) {'file

我有以下YAML文件,其中包含与文件名匹配的解析逻辑:

rules\u container=“”
文件名:
规则_1:{module:str,method:startswith,args:[s_]}
规则2:{module:str,method:endswith,args:[.log]}
逻辑:规则1和规则2
"""
我加载它并得到以下映射:

>>导入yaml
>>>导入pprint
>>d_yml=yaml.安全装载(规则容器)
>>>pprint.pprint(d_yml)
{'file_name':{'logic':'rule_1和rule_2',
'rule_1':{'args':['s_'],
'method':'startswith',
'module':'str'},
'rule_2':{'args':['.log'],
'方法':'endswith',
'module':'str'}}
下面的函数帮助我解析上述规则,最终我得到:

  • 汇编规则词典
  • 将规则应用于字符串(即文件名)的逻辑
def rule_编译器(模块、方法、args=None、kwargs=None):
def str_解析器(字符串):
返回getattr(字符串,方法)(*args)
返回stru解析器
def规则解析器(容器):
编译的规则={
k:k的规则编译器(**v),容器中的k,v。items()如果k.startswith('rule'))
}
logic=container.get('logic',None)
返回编译的规则、逻辑
注意:这里我没有使用
模块
kwargs
,但它们在配置文件的其他地方用于其他规则

因此,如果我在规则的容器上调用
rule\u parser()
,我会得到以下结果:

rp=rule\u解析器(d_yml['file\u name']) >>>打印(rp) ({'rule_1':, '规则2':}, '规则1和规则2') 如果我尝试用每一条规则解析字符串,它们会按预期工作:

fname='s_test.out' rp[0]['rule_1'](fname) 真的 >>>rp[0]['rule_2'](fname) 假的 我想应用“逻辑”定义的逻辑,并得到:

>>rp[0]['rule_1'](fname)和rp[0]['rule_2'](fname)
假的
我已经想到了一种方法,通过解析包含布尔逻辑的字符串,将其转换为与上面类似的内容,然后对其调用
eval()

你能想出不涉及
eval()
的其他方法吗

注意:可能有两个以上的规则,因此用一个简单的“and/or”替换逻辑,然后使用某种
if
语句来确定应用哪个规则也不起作用

注意:在这种特定情况下,使用正则表达式并完全消除对多个规则的需要不是一个选项

谢谢

这里有一个想法,使用,但不确定它在多大程度上适合您的情况

import pandas as pd

fname = 's_test.out'
rule, logic = rp

def apply_rule(rule_dict: dict, fname: str) -> dict:
    return {rule: func(fname) 
        for rule, func in rule_dict.items()}

print(pd.eval(logic, local_dict = apply_rule(rule, fname)))
输出:

False

正确的答案可以跨越任何地方,从使用类似于解析字符串的库(灵活性较差,可能是除
eval
之外最简单的选择),到从头开始阅读并实现一个库,或者使用解析器生成软件(最灵活,但困难且耗时)。在这种情况下,我可能会建议从一个专门为计算布尔表达式而构建的库开始,如果这不起作用,请尝试更复杂的方法


编辑:我应该注意,如果没有用户注入恶意代码的入口点,您可以使用
eval
,您可以正确清理eval字符串,等等-可能不值得您花费时间,但如果其他选项不适合您的需要,则值得考虑。

您可能会幸运地使用
ast.literal\u eval(…)
(未选中)。长格式正确的方法是构建一个解析器,该解析器读取您关心的语言,生成AST,然后构建AST节点的实现。参加一个大学级别的编译器设计课程,你将在课程结束时做好准备。但是,我不认为这是堆栈溢出问题的适当范围。任何合适的解析器生成器都可以让您设置适当的优先级规则,&c.@Jan,这肯定不是文字。我不希望
literal\u eval
工作。@Jan我得到一个
ValueError:ast.literal\u eval()
的节点或字符串格式不正确。不过,谢谢你的意见。我喜欢使用。我一直在努力使它工作,但我总是在这样一种情况下,我必须根据规则的名称动态创建变量。所以,我回到原点,只是有一个不同的。你可以使用字典来动态设置“变量”名称,或者预先创建一些变量,并将它们设置为你需要的值吗?我对这个库的使用的理解是,我需要达到一个点,我有一些类似于
rule_1,rule_2=algebra.symbols的东西(真、假)
,其中
True
False
startswith()和
endswith()的结果
方法。要实现这一点,我需要将规则的名称(可能会更改)作为函数名称空间的一部分。因此,我正在动态创建变量。否则,
逻辑中包含的字符串将不再像现在这样工作。即使我能够做到这一点,ARE规则的名称也将成为局部n的一部分amespace,我仍然无法从这个库中获得预期的结果。事实上,如果我做
algebra.parse(logic)==[True | False | True | False]
我总是得到
FALSE
。这似乎工作得很好。我不必解析/修改通过
logic
键提供的字符串,也不必担心创建一个有效的表达式来通过
eval()
执行。此外,如果我试图通过
逻辑传递一个“危险的”字符串(例如
“\uu导入”\uuuuuuuuuuuo('os')。listdir('.')”
)我得到一个异常
ValueError:“\uuuu import\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu