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

发布于 2024-08-06 19:56:19 字数 113 浏览 5 评论 0原文

有谁有一个使用 ast.NodeVisitor 来遍历 Python 2.6 中的抽象语法树的简单示例吗?我不清楚访问和 generic_visit 之间的区别,而且我找不到任何使用谷歌代码搜索或普通谷歌的示例。

Does anyone have a simple example using ast.NodeVisitor to walk the abstract syntax tree in Python 2.6? The difference between visit and generic_visit is unclear to me, and I cannot find any example using google codesearch or plain google.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

花桑 2024-08-13 19:56:19

ast.visit ——当然,除非你在子类中重写它 ——当调用访问类 fooast.Node 时,如果该方法存在,则调用 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

但是假设我们不关心负载节点(及其子节点——如果它们有的话;-)。那么处理这个问题的简单方法可能是,例如:

>>> 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

或者,假设我们还想查看名称节点的实际名称; then...:

>>> 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 并让 ast 的 visit 代表我们进行调度。

ast.visit -- unless you override it in a subclass, of course -- when called to visit an ast.Node of class foo, calls self.visit_foo if that method exists, otherwise self.generic_visit. The latter, again in its implementation in class ast itself, just calls self.visit on every child node (and performs no other action).

So, consider, for example:

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

Here, we're overriding generic_visit to print the class name, but also calling up to the base class (so that all children will also be visited). So for example...:

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

emits:

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

But suppose we didn't care for Load nodes (and children thereof -- if they had any;-). Then a simple way to deal with that might be, e.g.:

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

Now when we're visiting a Load node, visit dispatches, NOT to generic_visit any more, but to our new visit_Load... which doesn't do anything at all. So:

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

or, suppose we also wanted to see the actual names for Name nodes; then...:

>>> 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

But, NodeVisitor is a class because this lets it store information during a visit. Suppose all we want is the set of names in a "module". Then we don't need to override generic_visit any more, but rather...:

>>> 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']

This kind of thing is a more typical use case than ones requiring overrides of generic_visit -- normally, you're only interested in a few kinds of nodes, like we are here in Module and Name, so we can just override visit_Module and visit_Name and let ast's visit do the dispatching on our behalf.

旧时模样 2024-08-13 19:56:19

查看 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'))

打印出来

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()

Looking at the code in ast.py it's not that hard to copy paste and roll your own walker. E.g.

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'))

Prints out

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()
她说她爱他 2024-08-13 19:56:19

当找不到自定义访问者(即visit_Name)时,将调用generic_visit。这是我最近用 ast.NodeVisitor 编写的一段代码: https ://foss.heptapod.net/pypy/pypy/-/blob/80ead76ab428100ffeb01109c7fc0d94f1048af2/py/_code/_assertionnew.py 它解释 AST 节点以获取有关其中一些节点的调试信息,并使用 generic_visit 当未提供特殊实现时。

generic_visit is called when a custom visitor (ie visit_Name) can't be found. Here's a piece of code I wrote recently with ast.NodeVisitor: https://foss.heptapod.net/pypy/pypy/-/blob/80ead76ab428100ffeb01109c7fc0d94f1048af2/py/_code/_assertionnew.py It interprets the AST nodes to gain debugging information about some of them and falls back in with generic_visit when a special implementation isn't provided.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文