2013-08-16 2 views
2

Я играю с АСТ-манипуляциями. В настоящее время я пытаюсь удалить определенные узлы из ввода AST. Думаю, класс NodeTransformer является подходящим инструментом для этой цели. К сожалению, он не ведет себя так, как ожидалось.Python NodeTransformer: как удалить узлы?

documetation говорит:

«NodeTransformer будет ходить AST и использовать возвращаемое значение методов для посетителей, чтобы заменить или удалить старый узел Если возвращаемое значение метода посетителя не является ни один узел будет. удаляться из своего местоположения, в противном случае он заменяется возвращаемым значением ».

Теперь посмотрим на мою программу:

import _ast 
import ast 
import sys 

#ast transformer 
class MyTransformer(ast.NodeTransformer): 

    def iterate_children(self, node): 
     """ 
     helper 
     """ 
     children = ast.iter_child_nodes(node) 
     for c in children: 
      self.visit(c) 

    def generic_visit(self, node): 
     """ 
     default behaviour 
     """ 
     print("visiting: "+node.__class__.__name__) 
     self.iterate_children(node) 
     return node 

    def visit_For(self, node): 
     """ 
     For nodes: replace with nothing 
     """ 
     print("removing a For node") 
     return None 



#read source program 
filename = sys.argv[1] 
with open (filename, "r") as myfile: 
    source = str(myfile.read()) 

#compile source to ast 
m = compile(source, "<string>", "exec", _ast.PyCF_ONLY_AST) 

#do ast manipulation 
t = MyTransformer() 
t.visit(m) 

# fix locations 
m = ast.fix_missing_locations(m) 

#visualize the resulting ast 
#p = AstPrinter() 
#p.fromAst(m) 

#execute the transformed program 
print("computing...") 
codeobj = compile(m, '<string>', 'exec') 
exec(codeobj) 

Здесь входной файл:

l = [0, 1, 2, 3] 

total = 0 

for i in l: 
    total += i 

print(total) 

И результат:

visiting: Module 
visiting: Assign 
visiting: Name 
visiting: Store 
visiting: List 
visiting: Num 
visiting: Num 
visiting: Num 
visiting: Num 
visiting: Load 
visiting: Assign 
visiting: Name 
visiting: Store 
visiting: Num 
removing a For node 
visiting: Expr 
visiting: Call 
visiting: Name 
visiting: Load 
visiting: Name 
visiting: Load 
computing... 
6 

Я ожидал '0', потому что цикл был удален. Но есть «6» (= 0 + 1 + 2 + 3).

Кто-нибудь знает почему?

Python версии: 3.2.3

ast illustration

Числа в() указывают номер строки в программе ввода. Код рисунка изображения здесь не указан; пожалуйста, игнорируйте «корневой» узел. Как вы можете видеть, цикл For все еще существует.

Спасибо за чтение!

Update 21,8 .:

Я разместил ссылку на этот вопрос на питона ([email protected] рассылки). Похоже, я слишком много переписывал. Без посетителя для детей он работает так, как ожидалось.

Весь исходный код MyTransformer:

class MyTransformer(ast.NodeTransformer): 
    def visit_For(self, node): 
     """ 
     For nodes: replace with nothing 
     """ 
     print("removing a For node") 
     return None 

ответ

1

Нет, она работает правильно, потому что вы удалили самостоятельно написанный generic_visit(). Как вы можете видеть в исходном коде ast.py, NodeTransformer является дочерним элементом NodeVisitor, который имеет свой собственный метод generic_visit(). Этот метод выполняет обновление ваших узлов ast, и если вы переопределите этот метод, вы должны знать, для чего вы это делаете. Переопределение изменит всю логику NodeTransformer.

Если вам все еще нужно переопределить generic_visit() (например, для печати сообщений, как visiting: <AST object> при посещении узла), вы должны вызвать родительский метод в вашем generic_visit(). Таким образом, ваш метод будет следующим:

def generic_visit(self, node): 
     """ 
     printing visit messages 
     """ 
     super().generic_visit(node) 
     print("visiting: "+node.__class__.__name__) 
     self.iterate_children(node) 
     return node 

iterate_children() не влияет на результат в этом случае, но и должны быть удалены. Это заставляет посетителя запускать над детьми каждого узла.Но generic_visit() уже работает по всем узлам. Итак, с iterate_children() вы посещаете несколько узлов более одного раза. Это отнимает вычислительное время и может давать ошибки в более сложных случаях.

Смежные вопросы