2009-10-04 5 views

Antwort

68

ast.visit - es sei denn, Sie es in einer Unterklasse, natürlich außer Kraft setzen - wenn ein ast.Node der Klasse foo besuchen genannt, ruft self.visit_foo wenn diese Methode existiert, sonst self.generic_visit. Letzteres ruft wiederum in seiner Implementierung in Klasse ast selbst self.visit auf jedem Kindknoten auf (und führt keine andere Aktion aus).

So, betrachten, zum Beispiel:

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

Hier sind wir generic_visit zwingende den Klassennamen zu drucken, aber auch auf die Basisklasse aufrufen (so dass alle Kinder auch besucht wird). So zum Beispiel ...:

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

aussendet:

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

Aber nehmen wir nicht für Load Knoten scherte (und Kinder davon - wenn sie hatten jede ;-). Dann wird eine einfache Art und Weise damit umgehen könnte, zB:

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

Wenn wir jetzt einen Last Knoten besuchen, visit Depeschen, nicht mehr generic_visit, aber auf unsere neue visit_Load ... die ‚doesn t machen überhaupt nichts. Also:

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

oder, nehmen wir an, wir wollten auch die tatsächlichen Namen für Name Knoten sehen; dann ...:

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

Aber NodeVisitor ist eine Klasse, weil es Informationen während eines Besuchs speichern kann. Angenommen, wir wollen nur die Menge der Namen in einem "Modul". Dann brauchen wir nicht generic_visit mehr außer Kraft zu setzen, sondern ...:

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

Diese Art der Sache ist ein typischer Anwendungsfall als solche Überschreibungen von generic_visit erfordern - in der Regel sind Sie daran interessiert sind nur in ein paar Arten von Knoten, wie wir hier in Modul und Name sind, so können wir visit_Module und visit_Name gerade außer Kraft setzen und visit von ast die Disposition für uns vornehmen lassen.

+0

Vielen Dank! - genau das habe ich gesucht. – lacker

+1

@lacker, gern geschehen! –

+0

Toll, ich würde gerne ein bisschen mehr den Unterschied zwischen compiler.ast ans ast jetzt verstehen ... –

5

generic_visit wird aufgerufen, wenn ein benutzerdefinierter Besucher (dh visit_Name) nicht gefunden werden kann. Hier ist ein Stück Code, den ich kürzlich mit ast.NodeVisitor geschrieben habe: https://github.com/pypy/pypy/blob/master/py/_code/_assertionnew.py Er interpretiert die AST-Knoten, um Debugging-Informationen zu einigen von ihnen zu erhalten, und fällt mit generic_visit zurück, wenn keine spezielle Implementierung bereitgestellt wird.

12

Mit Blick auf den Code in ast.py ist es nicht so schwer, Paste und Rolle eigene Rollator zu kopieren. Z.B.

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

Druckt

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