Hat jemand ein einfaches Beispiel mit ast.NodeVisitor, um den abstrakten Syntaxbaum in Python 2.6 durchzugehen? Der Unterschied zwischen visit und generic_visit ist mir unklar, und ich kann kein Beispiel mit Google Codesearch oder einfach google finden.Einfaches Beispiel für die Verwendung von ast.NodeVisitor?
Antwort
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.
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.
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()
Vielen Dank! - genau das habe ich gesucht. – lacker
@lacker, gern geschehen! –
Toll, ich würde gerne ein bisschen mehr den Unterschied zwischen compiler.ast ans ast jetzt verstehen ... –