Ejemplo sencillo de cómo usar ast.¿NodeVisitor?


¿Alguien tiene un ejemplo simple usando ast?NodeVisitor para recorrer el árbol de sintaxis abstracta en Python 2.6? La diferencia entre visit y generic_visit no está clara para mí, y no puedo encontrar ningún ejemplo usando google codesearch o simplemente Google.

Author: lacker, 2009-10-04

3 answers

ast.visit -- a menos que lo sobreescribas en una subclase, por supuesto when cuando se llama para visitar un ast.Node de clase foo, llama a self.visit_foo si ese método existe, de lo contrario self.generic_visit. Este último, de nuevo en su implementación en la propia clase ast, solo llama a self.visit en cada nodo hijo (y no realiza ninguna otra acción).

Entonces, consideremos, por ejemplo:{[24]]}

>>> class v(ast.NodeVisitor):
...   def generic_visit(self, node):
...     print type(node).__name__
...     ast.NodeVisitor.generic_visit(self, node)
... 

Aquí, estamos sobreescribiendo generic_visit para imprimir el nombre de la clase, pero también llamando a la clase base (para que todos los hijos también ser visitado). Así, por ejemplo...:

>>> x = v()
>>> t = ast.parse('d[x] += v[y, x]')
>>> x.visit(t)

Emite:

Module
AugAssign
Subscript
Name
Load
Index
Name
Load
Store
Add
Subscript
Name
Load
Index
Tuple
Name
Load
Name
Load
Load
Load

Pero supongamos que no nos preocupamos por los nodos de carga (y sus hijos thereof si tenían alguno;-). Entonces una manera simple de lidiar con eso podría ser, por ejemplo:

>>> class w(v):
...   def visit_Load(self, node): pass
... 

Ahora cuando estamos visitando un nodo de carga, visit despachos, NO a generic_visit más, sino a nuestro nuevo visit_Load... que no hace nada en absoluto. Entonces:

>>> y = w()
>>> y.visit(t)
Module
AugAssign
Subscript
Name
Index
Name
Store
Add
Subscript
Name
Index
Tuple
Name
Name

O, supongamos que también queremos ver los nombres reales de los nodos de nombre; entonces...:

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

Pero, NodeVisitor es una clase porque esto le permite almacenar información durante una visita. Supongamos que todo lo que queremos es el conjunto de nombres en un "módulo". Entonces ya no necesitamos anular generic_visit, sino más bien...:

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

Este tipo de cosas es un caso de uso más típico que los que requieren sobreescrituras de generic_visit normally normalmente, solo estás interesado en unos pocos tipos de nodos, como estamos aquí en Módulo y Nombre, por lo que solo podemos sobreescribir visit_Module y visit_Name y dejar que ast visit hacer el envío en nuestro nombre.

 69
Author: Alex Martelli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-10-04 02:05:23

Mirando el código en ast.py no es tan difícil copiar, pegar y rodar su propio andador. Por ejemplo,

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

Imprime

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()
 12
Author: ubershmekel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-15 05:17:50

generic_visit se llama cuando no se puede encontrar un visitante personalizado (es decir, visit_Name). Aquí hay un fragmento de código que escribí recientemente con Ast.NodeVisitor: https://bitbucket.org/pypy/pypy/src/6df19fd2b6df6058daf162100cf7ee4521de5259/py/_code/_assertionnew.py?at=default&fileviewer=file-view-default Interpreta los nodos AST para obtener información de depuración sobre algunos de ellos y vuelve a utilizar generic_visit cuando no se proporciona una implementación especial.

 5
Author: Benjamin Peterson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-31 01:26:11