ast.visit
-当然,除非您在子类中覆盖了它- 当被调用以访问ast.Node
class的时foo
,self.visit_foo
如果存在该方法,则调用,否则self.generic_visit
。后者再次在类ast
本身的实现中,仅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
但是,假设我们不在乎Load节点(及其子节点-如果有的话;-)。那么一个简单的处理方法可能是:
>>> class w(v):
... def visit_Load(self, node): pass
...
现在,当我们访问一个Load节点时,不再visit
向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
-通常情况下,你只在几个不同的节点有兴趣,像我们这里的模块和名称,所以我们就可以覆盖visit_Module
并visit_Name
让AST的visit
做代表我们派遣。