Подтвердить что ты не робот

Простой пример использования ast.NodeVisitor?

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

4b9b3361

Ответ 1

ast.visit - если вы не переопределите его в подклассе, конечно - при вызове, чтобы посетить ast.Node класса foo, вызывает self.visit_foo, если этот метод существует, в противном случае self.generic_visit. Последний, снова в своей реализации в самом классе ast, просто вызывает self.visit для каждого дочернего node (и не выполняет никаких других действий).

Итак, рассмотрим, например:

>>> 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 node, 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, и пусть ast visit отправит от нашего имени.

Ответ 2

Глядя на код в 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()

Ответ 3

generic_visit вызывается, когда пользовательский посетитель (т.е. visit_Name) не может быть найден. Вот фрагмент кода, который я написал недавно с ast.NodeVisitor: https://github.com/pypy/pypy/blob/master/py/_code/_assertionnew.py Он интерпретирует узлы AST для получения отладочной информации о некоторых из них и возвращается с generic_visit, когда особая реализация не предоставляется.