如何使用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_Module
和visit_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
。