[Python] AST(抽象構文木)からコードを生成する

python の標準ライブラリから取得できるAST(抽象構文木)からpython のコードを生成してみます。

環境

python3.7.2

ASTとは

Abstract Syntax Treeの略で、日本語にすると抽象構文木です。

astor

https://github.com/berkerpeksag/astor

今回、ASTからコードを生成するのに利用するのはastorというライブラリです。

pipでインストールすることができます。

pip install astor

今回利用するバージョンは、astor==0.8.1です。

ASTを得る

ASTを得るには標準モジュールのastを利用します。

import ast

input_source = """

def hoge(a,b=1):
  return a+b

a = 1

print(hoge(a))
"""

ast_object = ast.parse(input_source)

ast.parse関数で文字列で与えたPythonコードのASTが得られます。

ast.dump関数を利用すると、どのようなASTに変換されたか、若干見やすくなります。

print(ast.dump(ast.object))
Module(
    body=[
        FunctionDef(
            name='hoge', 
            args=arguments(
                args=[
                    arg(arg='a', annotation=None),
                    arg(arg='b', annotation=None)
                ],
                vararg=None,
                kwonlyargs=[], 
                kw_defaults=[], 
                kwarg=None, 
                defaults=[Num(n=1)]
            ), 
            body=[Return(
                        value=BinOp(
                            left=Name(id='a', ctx=Load()), 
                            op=Add(), 
                            right=Name(id='b', ctx=Load())
                        )
                    )],
            decorator_list=[], 
            returns=None
        ),
        Assign(
            targets=[Name(id='a', ctx=Store())], 
            value=Num(n=1)
        ), 
        Expr(
            value=Call(
                func=Name(id='print', ctx=Load()), 
                args=[
                    Call(
                        func=Name(id='hoge', ctx=Load()), 
                        args=[Name(id='a', ctx=Load())], 
                        keywords=[]
                    )
                ], 
                keywords=[]
            )
        )
    ]
)

※実際には一行で表示されます。見やすくするため整形しています。

ASTからコードを生成する

先ほど得た、ASTからコードを生成してみます。

import astor

source = astor.to_source(ast_object)

print(source)
def hoge(a, b=1):
    return a + b


a = 1
print(hoge(a))

astor.to_source関数にASTを渡すことで、見事Pythonコードになりました。

例として簡単なコードしか挙げていませんが、クラスを利用したコードでも問題なく生成できました。
ASTを直接いじることで、いろいろと楽しいことができそうです。

さいごに

今回は、ASTからコードを生成する方法をメインに紹介したので、ASTからコードを生成するメリットを感じ無かったかも知れませんが、ASTに直接手を加えることでただの文字列置換より高度なコードの置き換えが出来たりするメリットがあったりします。 他の言語からの変換の際に便利だったりします。

より詳しく知りたい場合は、公式ドキュメントを参照してみてください。
https://astor.readthedocs.io/

以上、PythonのASTからPythonのコードを得る方法の紹介でした。

2020/12/6追記 @rhoboro様より教えていただきました。

Python3.9.0 からは ast.unparse関数が追加されastorライブラリを用いなくてもASTからソースコードの文字列を得ることが出来るようになりました。
https://docs.python.org/ja/3/library/ast.html#ast.unparse