web-dev-qa-db-ja.com

ast.NodeVisitorの使い方の簡単な例?

Ast.NodeVisitorを使用してPython 2.6の抽象構文ツリーをウォークする簡単な例はありますか?訪問とgeneric_visitの違いは不明瞭です。プレーングーグル。

43
lacker

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をオーバーライドしていますが、alsoで基本クラスを呼び出しています(すべての子もアクセスされるようになっています)。だから例えば...:

>>> 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ノードにアクセスすると、visitgeneric_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に代わりにディスパッチを実行させます。

78
Alex Martelli

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

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でフォールバックします。提供されていません。

6