在Python AST中,使用ast.NodeVisitor能否访问节点两次或改变遍历顺序?

6 投票
2 回答
3987 浏览
提问于 2025-04-16 15:01

NodeVisitor 是一种工具,它会按照深度优先的方式遍历抽象语法树(AST),每个节点只会在进入时被访问一次。因此,如果想要在这个过程中做一些复杂的事情,就会有点麻烦。那有没有办法改变它的默认行为呢?

2 个回答

13

也许有人会对DSblizzard提议的内容感兴趣,这里有一个简单的示例,来展示如何实现这个想法:

import ast

class RecursiveVisitor(ast.NodeVisitor):
    """ example recursive visitor """

    def recursive(func):
        """ decorator to make visitor work recursive """
        def wrapper(self,node):
            func(self,node)
            for child in ast.iter_child_nodes(node):
                self.visit(child)
        return wrapper

    @recursive
    def visit_Assign(self,node):
        """ visit a Assign node and visits it recursively"""
        print(type(node).__name__)

    @recursive
    def visit_BinOp(self, node):
        """ visit a BinOp node and visits it recursively"""
        print(type(node).__name__)

    @recursive
    def visit_Call(self,node):
        """ visit a Call node and visits it recursively"""
        print(type(node).__name__)

    @recursive
    def visit_Lambda(self,node):
        """ visit a Function node """
        print(type(node).__name__)

    @recursive
    def visit_FunctionDef(self,node):
        """ visit a Function node and visits it recursively"""
        print(type(node).__name__)

    @recursive
    def visit_Module(self,node):
        """ visit a Module node and the visits recursively"""
        pass

    def generic_visit(self,node):
        pass

class SimpleVisitor(ast.NodeVisitor):
    """ simple visitor for comparison """

    def recursive(func):
        """ decorator to make visitor work recursive """
        def wrapper(self,node):
            func(self,node)
            for child in ast.iter_child_nodes(node):
                self.visit(child)
        return wrapper

    def visit_Assign(self,node):
        """ visit a Assign node """
        print(type(node).__name__)

    def visit_BinOp(self, node):
        """ visit a BinOp node """
        print(type(node).__name__)

    def visit_Call(self,node):
        """ visit a Call node """
        print(type(node).__name__)

    def visit_Lambda(self,node):
        """ visit a Function node """
        print(type(node).__name__)

    def visit_FunctionDef(self,node):
        """ visit a Function node """
        print(type(node).__name__)

    @recursive
    def visit_Module(self,node):
        """ visit a Module node and the visits recursively, otherwise you
        wouldn't see anything here"""
        pass

    def generic_visit(self,node):
        pass

# usage example
a = """
b= lambda x: x*5 +5
def hhh(u):
    b=19
    return u*b
m=hhh(9*4+5)
"""

recursive_visitor = RecursiveVisitor()
simple_visitor = SimpleVisitor()
tree = ast.parse(a)
print('\nvisit recursive\n')
recursive_visitor.visit(tree)
print('\nvisit simple\n')
simple_visitor.visit(tree)

希望这个示例对某些人有帮助。

5

不行。你应该自己写一个访问器。递归地访问所有节点其实很简单,所以如果你用的ast模块没有提供你想要的功能(而且这个模块里确实没有你需要的东西),自己动手做也没那么难。

撰写回答