如何使用ast.NodeVisitor的简单例子?

有没有人有一个简单的例子,使用ast.NodeVisitor走Python 2.6中的抽象语法树? 访问和generic_visit之间的区别,我不清楚,我找不到任何使用谷歌codesearch或谷歌的例子。

ast.visit – 除非在子类中覆盖它 – 当调用访问类foo self.visit_foo如果该方法存在,则调用self.visit_foo ,否则self.generic_visit 。 后者,再次在其自身的类实现,只是在每个子节点上调用self.visit (并且不执行其他操作)。

所以,考虑一下,例如:

 >>> class v(ast.NodeVisitor): ... def generic_visit(self, node): ... print type(node).__name__ ... ast.NodeVisitor.generic_visit(self, node) ... 

在这里,我们覆盖了generic_visit来打印类名,但调用基类(以便所有的孩子也将被访问)。 所以例如…:

 >>> x = v() >>> t = ast.parse('d[x] += v[y, x]') >>> x.visit(t) 

发出:

 Module AugAssign Subscript Name Load Index Name Load Store Add Subscript Name Load Index Tuple Name Load Name Load Load Load 

但是,假设我们不关心负载节点(和它的孩子 – 如果他们有任何;-)。 那么处理这个问题的一个简单的方法可能是,例如:

 >>> class w(v): ... def visit_Load(self, node): pass ... 

现在,当我们访问Load节点时, visit dispatches,而不是访问generic_visit ,而是访问我们的新visit_Load …,这根本没有任何作用。 所以:

 >>> y = w() >>> y.visit(t) Module AugAssign Subscript Name Index Name Store Add Subscript Name Index Tuple Name Name 

或者,假设我们也想查看Name节点的实际名称; 然后…:

 >>> class z(v): ... def visit_Name(self, node): print 'Name:', node.id ... >>> z().visit(t) Module AugAssign Subscript Name: d Index Name: x Store Add Subscript Name: v Index Tuple Name: y Name: x Load Load 

但是,NodeVisitor是一个类,因为它可以在访问期间存储信息。 假设我们想要的是“模块”中的一组名称。 那么我们不需要重写generic_visit ,而是…:

 >>> class allnames(ast.NodeVisitor): ... def visit_Module(self, node): ... self.names = set() ... self.generic_visit(node) ... print sorted(self.names) ... def visit_Name(self, node): ... self.names.add(node.id) ... >>> allnames().visit(t) ['d', 'v', 'x', 'y'] 

这种事情比需要覆盖generic_visit的更典型的用例 – 通常,你只对几种节点感兴趣,就像我们在Module和Name中这样,所以我们可以重写visit_Modulevisit_Name ,让我们的visit代表我们的派遣。

查看ast.py中的代码,复制粘贴并滚动自己的walker并不难。 例如

 import ast def str_node(node): if isinstance(node, ast.AST): fields = [(name, str_node(val)) for name, val in ast.iter_fields(node) if name not in ('left', 'right')] rv = '%s(%s' % (node.__class__.__name__, ', '.join('%s=%s' % field for field in fields)) return rv + ')' else: return repr(node) def ast_visit(node, level=0): print(' ' * level + str_node(node)) for field, value in ast.iter_fields(node): if isinstance(value, list): for item in value: if isinstance(item, ast.AST): ast_visit(item, level=level+1) elif isinstance(value, ast.AST): ast_visit(value, level=level+1) ast_visit(ast.parse('a + b')) 

打印出来

 Module(body=[<_ast.Expr object at 0x02808510>]) Expr(value=BinOp(op=Add())) BinOp(op=Add()) Name(id='a', ctx=Load()) Load() Add() Name(id='b', ctx=Load()) Load() 

当自定义访问者(即visit_Name)不能被find时, generic_visit被调用。 这是我最近用ast.NodeVisitor写的一段代码: https : //github.com/pypy/pypy/blob/master/py/_code/_assertionnew.py它解释AST节点以获得关于其中一些的debugging信息,如果没有提供特殊的实现,则返回到generic_visit