ast.visit
- если, конечно, вы не переопределите его в подклассе - когда вызывается для посещения ast.Node
класса foo
, вызывает self.visit_foo
, если этот метод существует, в противном случае self.generic_visit
. Последний, опять же в своей реализации в самом классе ast
, просто вызывает self.visit
на каждом дочернем узле (и не выполняет никаких других действий).
Итак, рассмотрим, например:
>>> 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
Но предположим, что нас не заботили узлы Load (и их потомки - если они были ;-). Тогда простой способ справиться с этим может быть, например ::
>>> class w(v):
... def visit_Load(self, node): pass
...
Теперь, когда мы посещаем узел Load, 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
и позвольте visit
ast выполнить диспетчеризацию от нашего имени.