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

lac*_*ker 43 python abstract-syntax-tree python-2.6

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

Ale*_*lli 78

ast.visit- 除非你在子类中覆盖它,当然 - 当调用访问ast.Node类时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)
... 
Run Code Online (Sandbox Code Playgroud)

在这里,我们重写generic_visit打印类名,但调用基类(这样也将访问所有子项).所以例如...:

>>> x = v()
>>> t = ast.parse('d[x] += v[y, x]')
>>> x.visit(t)
Run Code Online (Sandbox Code Playgroud)

发出:

Module
AugAssign
Subscript
Name
Load
Index
Name
Load
Store
Add
Subscript
Name
Load
Index
Tuple
Name
Load
Name
Load
Load
Load
Run Code Online (Sandbox Code Playgroud)

但是假设我们不关心Load节点(及其子节点 - 如果它们有任何;-).然后一个简单的方法来解决这个问题,例如:

>>> class w(v):
...   def visit_Load(self, node): pass
... 
Run Code Online (Sandbox Code Playgroud)

现在,当我们访问一个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
Run Code Online (Sandbox Code Playgroud)

或者,假设我们还想查看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
Run Code Online (Sandbox Code Playgroud)

但是,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']
Run Code Online (Sandbox Code Playgroud)

这种事情是一个比较典型的使用情况不是要求的覆盖那些generic_visit-通常情况下,你只是在几个不同的节点有兴趣,像我们这里的模块和名称,所以我们就可以覆盖visit_Modulevisit_Name让AST的visit做代表我们派遣.


ube*_*kel 12

查看ast.py中的代码,复制粘贴和滚动自己的助行器并不困难.例如

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'))
Run Code Online (Sandbox Code Playgroud)

打印出来

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()
Run Code Online (Sandbox Code Playgroud)


Ben*_*son 6

generic_visit在无法找到自定义访问者(即visit_Name)时调用.这是我最近用ast.NodeVisitor编写的一段代码:https://bitbucket.org/pypy/pypy/src/6df19fd2b6df6058daf162100cf7ee4521de5259/py/_code/_assertionnew.py ? at = default & fileviewer = file-view-default 它解释了AST节点获取有关其中一些的调试信息,并在generic_visit没有提供特殊实现时重新使用.