在AST python中用表达式中的实际值替换变量名

在AST python中用表达式中的实际值替换变量名,python,abstract-syntax-tree,Python,Abstract Syntax Tree,我用变量形式描述了一个表达式,如下所示 's1*3 - (s2-s1)*1' 我已经给出了s1和s2的值,可以根据需要进行更改 我可以使用python模块通过替换相应的s1和s2值(s1=20,s2=30)来计算这个表达式 导入ast 作为op导入运算符 运算符={ast.Add:op.Add,ast.Sub:op.Sub,ast.Mult:op.mul, ast.Div:op.truediv,ast.Pow:op.Pow,ast.BitXor:op.xor, ast.USub:op.neg}

我用变量形式描述了一个表达式,如下所示

's1*3 - (s2-s1)*1'
我已经给出了s1和s2的值,可以根据需要进行更改

我可以使用python模块通过替换相应的s1和s2值(s1=20,s2=30)来计算这个表达式

导入ast
作为op导入运算符
运算符={ast.Add:op.Add,ast.Sub:op.Sub,ast.Mult:op.mul,
ast.Div:op.truediv,ast.Pow:op.Pow,ast.BitXor:op.xor,
ast.USub:op.neg}
def eval_3;(节点):
如果isinstance(节点,ast.Num):#
返回节点
elif isinstance(节点,ast.BinOp):#
返回运算符[类型(node.op)](eval_(node.left)、eval_(node.right))
elif isinstance(节点,ast.UnaryOp):#例如,-1
返回运算符[类型(node.op)](eval(node.operand))
其他:
raise TYPE错误(节点)
>>>str1='20*3-(30-20)*1'
>>>node=ast.parse(str1,mode='eval')
>>>评估(节点体)
50
我应该如何计算这个表达式,而不需要将变量替换为它们的实际值


谢谢

您可以使用
eval
功能。但在使用
eval
时必须小心,因为它执行任何字符串,如果您接受字符串以从不受信任的输入中求值,则可能非常危险。例如,假设正在计算的字符串是“
”os.system('rm-rf/”)”
?它将真正开始删除计算机上的所有文件

>>> eval('20*3 - (30-20)*1')
50 
作为更好的解决方案,您可以使用python的内部

因此,如果您想计算公式,作为一种比使用
input
更安全的方法,您可以使用
compile
和eval

>>> eq='s1*3 - (s2-s1)*1'
>>> a=compile(eq,'','eval')
>>> eval(a)
50

您还可以使用这是一个用于符号数学的Python库。它的目标是成为一个功能齐全的计算机代数系统(CAS),同时使代码尽可能简单,以便理解和易于扩展。Symphy完全用Python编写,不需要任何外部库

要计算表达式,可以使用
NodeVisitor

from ast import NodeVisitor

class EvalVisitor(NodeVisitor):
    def __init__(self, **kwargs):
        self._namespace = kwargs

    def visit_Name(self, node):
        return self._namespace[node.id]

    def visit_Num(self, node):
        return node.n

    def visit_NameConstant(self, node):
        return node.value

    def visit_UnaryOp(self, node):
        val = self.visit(node.operand)
        return operators[type(node.op)](val)

    def visit_BinOp(self, node):
        lhs = self.visit(node.left)
        rhs = self.visit(node.right)
        return operators[type(node.op)](lhs, rhs)

    def generic_visit(self, node):
        raise ValueError("malformed node or string: " + repr(node))
您可以将此计算器用作

v = EvalVisitor(s1=20, s2=30)
print(v.visit(node.body))
这大致就是
ast.literal_eval
的实现方式,添加了允许您传入值和不计算非数值表达式的功能


顺便说一句,这本
操作符
字典的技巧不错。我会把这一个复制到聚会上:)

晚一点,但对于感兴趣的人来说:通过对OP代码的轻微修改也可以实现这一点(顺便说一下,这看起来非常类似)。这是:

eval
函数定义中,添加另一个
elif
,如下所示:

...
elif isinstance(node, ast.Name):
    return operators[node.id]
...
然后,您可以简单地将变量添加到
运算符中

>>> s1=20
>>> s2=30
>>> operators['s1']=s1
>>> operators['s2']=s2
>>> node = ast.parse('s1*3 - (s2-s1)*1', mode='eval')
>>> eval_(node.body)
50

道具应根据
asteval
模块转到。另请参见。

我希望编译's1*3-(s2-s1)*1'而不是'20*3-(30-20)*1'。。Thanks@AnuragSharma使用
ast
时,您实际上走在了正确的轨道上<代码>编译器
自Python 2.6以来就被弃用,在Python 3中不再存在。我将根据
ast
添加一个答案。执行“v=EvalVisitor(s1=20,s2=30)”会向我抛出一个错误“TypeError:super()至少接受一个参数(0给定)”啊,对不起,如果您使用的是Python 2,那么它必须是
super(EvalVisitor,self)。\uu init\uuuu()
。实际上,我甚至不确定是否需要这一行,我不认为
ast.NodeVisitor
有一个非常重要的构造函数。我将编辑答案。
...
elif isinstance(node, ast.Name):
    return operators[node.id]
...
>>> s1=20
>>> s2=30
>>> operators['s1']=s1
>>> operators['s2']=s2
>>> node = ast.parse('s1*3 - (s2-s1)*1', mode='eval')
>>> eval_(node.body)
50