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