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

时间:2014-10-16 07:06:25

标签: python abstract-syntax-tree

我有一个像这样的变量形式描述的表达式

's1*3 - (s2-s1)*1'

我给出了s1和s2的值,可以根据需要改变

我可以使用python ast模块通过替换相应的s1和s2值来评估此表达式(s1 = 20,s2 = 30)

import ast
import operator as op

operators = {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_(node):
    if isinstance(node, ast.Num): # <number>
        return node.n
    elif isinstance(node, ast.BinOp): # <left> <operator> <right>
        return operators[type(node.op)](eval_(node.left), eval_(node.right))
    elif isinstance(node, ast.UnaryOp): # <operator> <operand> e.g., -1
        return operators[type(node.op)](eval_(node.operand))
    else:
    raise TypeError(node)

>>> str1 = '20*3 - (30-20)*1'
>>> node = ast.parse(str1, mode='eval')
>>> eval_(node.body)
50

如何评估此表达式而无需将变量替换为实际值。

由于

3 个答案:

答案 0 :(得分:4)

要评估表达式,您可以使用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的实现方式,增加的功能是允许您在评估非数字表达式时传递值。

顺便说一下,这个operators字典很好用。我将复制那个:)

答案 1 :(得分:2)

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

>>> eval('20*3 - (30-20)*1')
50 

作为一个更好的解决方案,您可以使用python的内部compiler来解析您的等式:

>>> s1=20
>>> s2=30
>>> eq='s1*3 - (s2-s1)*1'
>>> compiler.parse( eq )
Module(None, Stmt([Discard(Sub((Mul((Name('s1'), Const(3))), Mul((Sub((Name('s2'), Name('s1'))), Const(1))))))]))

因此,如果您想评估等式,比使用input更安全,您可以使用compile和eval!

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

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

答案 2 :(得分:1)

派对有点晚了,但对于那些感兴趣的人:也可以通过略微修改OP的代码来实现这一点(顺便说一下,它与this看起来非常相似)。这是:

eval_函数定义中,按如下方式添加另一个elif

...
elif isinstance(node, ast.Name):
    return operators[node.id]
...

然后,您只需将变量添加到operators字典即可。使用OP的例子:

>>> 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模块转到this answer。另请参阅asteval source