2009-10-04 89 views
37

有沒有人有一個簡單的例子,使用ast.NodeVisitor來遍歷Python 2.6中的抽象語法樹?訪問和generic_visit之間的區別並不清楚,我找不到任何使用谷歌codesearch或谷歌簡單的例子。如何使用ast.NodeVisitor的簡單示例?

回答

68

ast.visit - 除非你覆蓋它的一個子類,當然 - 所謂的參觀foo類的ast.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 
... 

現在,當我們訪問一個負載節點,visit調度,而不是generic_visit了,而是我們的新visit_Load ......這沒有按」不要做任何事情。因此:

>>> y = w() 
>>> y.visit(t) 
Module 
AugAssign 
Subscript 
Name 
Index 
Name 
Store 
Add 
Subscript 
Name 
Index 
Tuple 
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,並讓ast的visit代表我們進行調度。

+0

謝謝! - 這正是我所期待的。 – lacker 2009-10-04 02:29:03

+1

@lacker,不客氣! – 2009-10-04 02:56:05

+0

太好了,我想了解更多的編譯器。現在的答案是什麼... – 2015-06-25 10:33:17

5

generic_visit在無法找到自定義訪問者(即visit_Name)時被調用。以下是我最近使用ast.NodeVisitor編寫的一段代碼:https://github.com/pypy/pypy/blob/master/py/_code/_assertionnew.py它解釋AST節點以獲取有關其中一些節點的調試信息,並在沒有提供特殊實現時使用generic_visit

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

打印出

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