3

私はAST操作で遊んでいます。現在、入力 AST から特定のノードを削除しようとしています。NodeTransformer クラスは、この目的に適したツールだと思います。残念ながら、期待どおりに動作しません。

ドキュメントには次のように書かれています。

「NodeTransformer は AST をたどり、ビジター メソッドの戻り値を使用して古いノードを置換または削除します。ビジター メソッドの戻り値が None の場合、ノードはその場所から削除されます。それ以外の場合は、戻り値。」

今私のプログラムを見てください:

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

アストイラスト

( )内の数字は、入力プログラムの行番号を示します。ここでは、画像描画のコードは提供しません。「ルート」ノードは無視してください。ご覧のとおり、For ループがまだ残っています。

読んでくれてありがとう!

更新 21.8.:

この質問へのリンクを python メーリングリスト (python-list@python.org) に投稿しました。どうも上書きしすぎたようです。子供の訪問者がいなくても、期待どおりに機能します。

MyTransformer のソース コード全体:

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

1 に答える 1

2

いいえ、自分で書いた を削除したため、正しく動作しますgeneric_visit()。のソース コードでわかるようにast.pyNodeTransformerは の子であり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()いくつかのノードに複数回アクセスします。これは計算時間を浪費し、より複雑なケースではエラーが発生する可能性があります。

于 2016-01-02T14:42:29.073 に答える