Простой пример использования ast.NodeVisitor? - PullRequest
41 голосов
/ 04 октября 2009

У кого-нибудь есть простой пример использования ast.NodeVisitor для обхода абстрактного синтаксического дерева в Python 2.6? Разница между посещением и generic_visit мне неясна, и я не могу найти ни одного примера с использованием Google Codesearch или простого Google.

Ответы [ 3 ]

74 голосов
/ 04 октября 2009

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

Здесь мы переопределяем 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

Но предположим, что нас не заботили узлы 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

или, предположим, мы также хотели увидеть фактические имена для узлов 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 и позвольте visit ast выполнить диспетчеризацию от нашего имени.

12 голосов
/ 15 октября 2014

Глядя на код в 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()
5 голосов
/ 04 октября 2009

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, когда не предусмотрена специальная реализация.

...