2013-08-16 2 views
2

저는 AST 조작으로 놀고 있습니다. 현재 입력 AST에서 특정 노드를 제거하려고합니다. NodeTransformer 클래스는이 목적을위한 적절한 툴이라고 생각합니다. 슬프게도 예상대로 동작하지 않습니다.Python NodeTransformer : 노드를 제거하는 방법?

documetation는 말한다 :

는 "NodeTransformer는 AST를 걸어 교체하거나 이전 노드를 제거하는 방문자 방법의 반환 값을 사용하는 방문자 메소드의 반환 값이 없음이없는 경우, 노드는 것이다. 해당 위치에서 제거해야합니다. 그렇지 않으면 반환 값으로 바뀝니다. "

지금 내 프로그램을보고 :

l = [0, 1, 2, 3] 

total = 0 

for i in l: 
    total += i 

print(total) 

그리고 결과 : 여기

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) 

입력 파일입니다
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)이 있습니다.

이유를 아는 사람이 있습니까?

파이썬 버전 : 3.2.3

ast illustration

숫자는() 입력 프로그램의 줄 번호를 나타냅니다. 이미지 그리기 코드는 여기에 제공되지 않습니다. "루트"노드를 무시하십시오. 보시다시피 For 루프가 아직 있습니다.

읽어 주셔서 감사합니다.

업데이트 21.8 :

나는 파이썬 메일 링리스트 ([email protected])에이 질문에 대한 링크를 게시했다. 너무 많이 겹쳐 쓴 것처럼 보입니다. 어린이 방문객이 없으면 예상대로 작동합니다. MyTransformer의

전체 소스 코드 : 당신이 자기 기록 generic_visit()을 제거하기 때문에

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

답변

1

아니, 그것은 제대로 작동합니다. ast.py의 소스 코드에서 볼 수 있듯이 NodeTransformergeneric_visit() 메서드를 가진 NodeVisitor의 하위 요소입니다. 이 메서드는 노드 ast의 업데이트를 수행하며이 메서드를 재정의하는 경우 수행중인 작업을 알고 있어야합니다. 재정의하면 NodeTransformer의 모든 로직이 변경됩니다.

여전히 (노드를 방문 할 때 예 visiting: <AST object> 같은 메시지를 인쇄 할 수) generic_visit()을 무시해야하는 경우, 당신은 당신의 generic_visit()에 부모 방법를 호출해야합니다. 그래서, 당신의 방법은 다음 될 것입니다 :

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

이 경우 결과에 영향을주지 않습니다, 또한 제거해야합니다. 방문자가 각 노드의 자식에 대해 을 실행하게합니다..그러나 generic_visit()은 이미 모든 노드에서 실행됩니다. 따라서 iterate_children()을 사용하면 일부 노드를 두 번 이상 방문합니다. 이것은 계산 시간을 낭비하고보다 복잡한 경우에 오류를 줄 수 있습니다.

관련 문제