2009-10-04 27 views
37

誰も、Python 2.6で抽象構文木を歩くためにast.NodeVisitorを使用する単純な例はありますか? visitとgeneric_visitの違いはわかりません。google codesearchやplain googleを使用した例は見つかりません。ast.NodeVisitorの使い方の簡単な例?

答えて

68

ast.visit - あなたはもちろん、サブクラスでそれを上書きしない限り - そのメソッドが存在する場合は、クラスfooast.Nodeを訪問すると呼ばれ、そうでない場合self.generic_visitself.visit_fooを呼び出します。後者は、クラス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しないが、「doesnの新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のオーバーライドを必要とするものより一般的なユースケースである - 通常は、あなただけの興味を持っていますモジュールと名前のようないくつかの種類のノードでは、visit_Modulevisit_Nameをオーバーライドし、私たちの代わりにastのvisitをディスパッチさせます。

+0

ありがとうございます!これはまさに私が探していたものです。 – lacker

+1

@lacker、よろしいですか? –

+0

素晴らしいですが、私は今、compiler.astとastの違いをもっと理解したいと思います... –

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