Prosty przykład użycia ast.NodeVisitor?

Czy ktoś ma prosty przykład z użyciem ast.NodeVisitor będzie chodził po abstrakcyjnym drzewie składni w Pythonie 2.6? Różnica między visit a generic_visit jest dla mnie niejasna i nie mogę znaleźć żadnego przykładu za pomocą Google codesearch lub zwykłego google.

Author: lacker, 2009-10-04

3 answers

ast.visit -- o ile nie nadpisujesz jej w podklasie , oczywiście -- gdy wywołane do odwiedzenia ast.Node klasy foo, wywoła self.visit_foo jeśli ta metoda istnieje, w przeciwnym razie self.generic_visit. Ten ostatni, ponownie w swojej implementacji w samej klasie ast, po prostu wywołuje self.visit na każdym węźle potomnym (i nie wykonuje żadnej innej akcji).

Rozważmy więc na przykład:

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

Tutaj nadpisujemy generic_visit, aby wydrukować nazwę klasy, ale również wywołując klasę bazową (tak, aby wszystkie dzieci również być odwiedzane). Na przykład...:

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

Emituje:

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

Ale przypuśćmy, że nie dbaliśmy o węzły obciążenia (i ich dzieci-jeśli takie miały; -). Następnie prostym sposobem radzenia sobie z tym może być np.:]}

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

Teraz, gdy odwiedzamy węzeł ładowania, visit wysyłamy, nie do generic_visit, ale do naszego nowego visit_Load... co nic nie robi. Więc:

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

Lub, przypuśćmy, że chcemy również zobaczyć rzeczywiste nazwy węzłów nazw; więc...:

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

Ale nodevisitor jest klasą, ponieważ pozwala na przechowywanie informacji podczas wizyty. Załóżmy, że wszystko, czego chcemy, to zbiór nazw w "module". Więc nie musimy już nadpisywać generic_visit, ale raczej...:

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

Tego typu rzeczy są bardziej typowymi przypadkami użycia niż te wymagające przesłonięcia generic_visit -- Zwykle interesują Cię tylko kilka rodzajów węzłów, jak my jesteśmy tutaj w Module I Name, więc możemy po prostu przesłonić visit_Module i visit_Name i niech ast visit zrób wysyłkę w naszym imieniu.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-04 02:05:23

Patrząc na kod w ast.py nie jest tak trudno skopiować wklej i wrzucić własny chodzik. Np.

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

Drukuje

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/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-15 05:17:50

generic_visit jest wywoływana, gdy Niestandardowy użytkownik (tj. nazwa wizytówki) nie może zostać znaleziony. Oto fragment kodu, który napisałem niedawno z ast.NodeVisitor: https://bitbucket.org/pypy/pypy/src/6df19fd2b6df6058daf162100cf7ee4521de5259/py/_code/_assertionnew.py?at=default&fileviewer=file-view-default interpretuje węzły AST, aby uzyskać informacje debugujące o niektórych z nich i wraca do generic_visit, gdy specjalna implementacja nie jest dostarczona.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-31 01:26:11