我有一个用Electron / Typescript编写的应用程序,我需要验证用户输入是否是有效的Python表达式。
例如:
cos(PARAMPOLY.engineeringValue1) + cos(PARAMPOLY.engineeringValue2)
x + y + z
我无法为这些表达式生成正确的操作数类型和值。我需要解析表达式的内容,并告诉我是否存在表达式错误。
Python eval()
函数解析并评估该表达式。我只需要解析。
有什么需要的吗?
答案 0 :(得分:2)
您可能希望将完整编译转换为完整的Python代码对象,也可以将其解析为抽象语法树。您可以使用compile()
function来实现,也可以只使用ast.parse()
来生成树。
解析为AST会标记输入,并输出语法对象树,然后可以对其进行进一步分析或转换。编译为字节码更进一步,使用该AST创建一个Python代码对象,您可以选择使用eval()
或exec()
function执行该对象;请注意,后者总是返回None
,可能不是评估表达式代码对象的最佳选择。
eval(string)
使用eval(compile(string, "<stdin>", "eval"))
来编译代码对象的字符串参数,然后执行它,因此compile(string, "<stdin>", "eval")
会给您相同的结果,而无需执行。
如果只有表达式有效,则使用"eval"
作为模式;如果要接受完整的Python 语句,则使用"exec"
。如果输入不是有效的Python表达式(compile()
)或无效的语句(ast.parse()
),则SyntaxError
(和"eval"
)会引发"exec"
异常。 / p>
演示:
>>> example1 = "cos(PARAMPOLY.engineeringValue1) + cos(PARAMPOLY.engineeringValue2)"
>>> example2 = "x + y + z"
>>> compile(example1, "<stdin>", "eval")
<code object <module> at 0x111c2eae0, file "<stdin>", line 1>
>>> compile(example2, "<stdin>", "eval")
<code object <module> at 0x111c2e540, file "<stdin>", line 1>
>>> result2 = _
>>> eval(result2, {"x": 42, "y": 81, "z": 117})
240
>>> compile("not a valid expression", "<stdin>", "eval")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1
not a valid expression
^
SyntaxError: invalid syntax
通过解析AST,您可以发现代码期望访问的名称;您可以通过查找Name
个节点来收集名称:
>>> import ast
>>> tree1 = ast.parse(example1)
>>> tree2 = ast.parse(example2)
>>> ast.dump(tree2.body[0])
"Expr(value=BinOp(left=Call(func=Name(id='cos', ctx=Load()), args=[Attribute(value=Name(id='PARAMPOLY', ctx=Load()), attr='engineeringValue1', ctx=Load())], keywords=[]), op=Add(), right=Call(func=Name(id='cos', ctx=Load()), args=[Attribute(value=Name(id='PARAMPOLY', ctx=Load()), attr='engineeringValue2', ctx=Load())], keywords=[])))"
>>> ast.dump(tree2.body[0])
"Expr(value=BinOp(left=BinOp(left=Name(id='x', ctx=Load()), op=Add(), right=Name(id='y', ctx=Load())), op=Add(), right=Name(id='z', ctx=Load())))"
>>> {node.id for node in ast.walk(tree1) if isinstance(node, ast.Name)}
{'cos', 'PARAMPOLY'}
>>> {node.id for node in ast.walk(tree2) if isinstance(node, ast.Name)}
{'x', 'z', 'y'}
请注意,上述忽略的上下文,因此也列出了PARAMPONLY
属性名称。如果您需要处理具有更多上下文的语法树,请编写ast.NodeVisitor
subclass。