Python 3.x 如何在python3中使用AST递归简化数学表达式?

Python 3.x 如何在python3中使用AST递归简化数学表达式?,python-3.x,recursion,abstract-syntax-tree,Python 3.x,Recursion,Abstract Syntax Tree,我有一个数学表达式: tree = ast.parse('1 + 2 + 3 + x') 对应于此抽象语法树: Module(body=[Expr(value=BinOp(left=BinOp(left=BinOp(left=Num(n=1), op=Add(), right=Num(n=2)), op=Add(), right=Num(n=3)), op=Add(), right=Name(id='x', ctx=Load())))]) 我想简化它,也就是说,得到这个: Module(bo

我有一个数学表达式:

tree = ast.parse('1 + 2 + 3 + x')
对应于此抽象语法树:

Module(body=[Expr(value=BinOp(left=BinOp(left=BinOp(left=Num(n=1), op=Add(), right=Num(n=2)), op=Add(), right=Num(n=3)), op=Add(), right=Name(id='x', ctx=Load())))])
我想简化它,也就是说,得到这个:

Module(body=[Expr(value=BinOp(left=Num(n=6), op=Add(), right=Name(id='x', ctx=Load())))])
根据,我应该使用NodeTransformer类。文件中的建议如下:

请记住,如果正在操作的节点具有子节点,则 必须自己变换子节点或调用 首先对节点使用泛型_visit()方法

我尝试实现我自己的转换器:

class Evaluator(ast.NodeTransformer):
    def visit_BinOp(self, node):
        print('Evaluating ', ast.dump(node))
        for child in ast.iter_child_nodes(node):
            self.visit(child)

        if type(node.left) == ast.Num and type(node.right) == ast.Num:
            print(ast.literal_eval(node))
            return ast.copy_location(ast.Subscript(value=ast.literal_eval(node)), node)
        else:
            return node
在这种特殊情况下,它应该做的是将1+2简化为3,然后将3+3简化为6。 它确实简化了我想要简化的二进制操作,但它不会更新原始语法树。我尝试了不同的方法,但仍然不知道如何递归地简化所有二进制操作(以深度优先的方式)。谁能给我指一下正确的方向吗


谢谢。

对于
访问*
方法,有三种可能的返回值:

  • None
    表示节点将被删除
  • 节点
    (节点本身),这意味着不会应用任何更改
  • 将替换旧节点的新节点

  • 因此,当您想用
    Num
    替换
    BinOp
    时,需要返回一个新的
    Num
    节点。表达式的计算不能通过
    ast.literal\u eval
    完成,因为此函数仅计算文字(而不是任意表达式)。例如,您可以使用
    eval

    因此,您可以使用以下节点转换器类:

    导入ast
    类计算器(ast.NodeTransformer):
    ops={
    ast.Add:“+”,
    ast.Sub:“-”,
    ast.Mult:“*”,
    ast.Div:“/”,
    #在这里定义更多
    }
    def visit_BinOp(自身,节点):
    self.generic_访问(节点)
    如果isinstance(node.left,ast.Num)和isinstance(node.right,ast.Num):
    
    #在Python上,我也尝试用ast.walk解决这个问题,但发现了相同的错误:我执行的更改不会持久。请注意,
    1+2+3+x
    在执行之前已经优化到
    6+x
    。反汇编(
    dis.dis('1+2+3+x')
    )允许看到这一点。“表达式的求值不能通过ast.literal\u eval完成,因为此函数只求值文本(而不是任意表达式)”,但我使用literal\u eval求值表达式(1+1),它返回2。这就是我想要的:在不涉及变量的情况下简化操作。如果您使用的是Python,我不能也使用literal_eval吗?@GuillermoMosse