32.2. ast
— 抽象構文木¶
ソースコード: Lib/ast.py
ast
モジュールは、Python アプリケーションで Python の抽象構文木を処理しやすくするものです。抽象構文そのものは、Python のリリースごとに変化する可能性があります。このモジュールを使用すると、現在の文法をプログラム上で知る助けになるでしょう。
抽象構文木を作成するには、 ast.PyCF_ONLY_AST
を組み込み関数 compile()
のフラグとして渡すか、あるいはこのモジュールで提供されているヘルパー関数 parse()
を使います。その結果は、 ast.AST
を継承したクラスのオブジェクトのツリーとなります。抽象構文木は組み込み関数 compile()
を使って Python コード・オブジェクトにコンパイルすることができます。
32.2.1. Node クラス¶
-
class
ast.
AST
¶ このクラスは全ての AST ノード・クラスの基底です。実際のノード・クラスは 後ほど 示す
Parser/Python.asdl
ファイルから派生したものです。これらのクラスは_ast
C モジュールで定義され、ast
にもエクスポートし直されています。抽象文法の左辺のシンボル一つずつにそれぞれ一つのクラスがあります (たとえば
ast.stmt
やast.expr
)。それに加えて、右辺のコンストラクタ一つずつにそれぞれ一つのクラスがあり、これらのクラスは左辺のツリーのクラスを継承しています。たとえば、ast.BinOp
はast.expr
から継承しています。代替を伴った生成規則 (production rules with alternatives) (別名 “sums”) の場合、左辺は抽象クラスとなり、特定のコンストラクタ・ノードのインスタンスのみが作成されます。-
_fields
¶ 各具象クラスは属性
_fields
を持っており、すべての子ノードの名前をそこに保持しています。具象クラスのインスタンスは、各子ノードに対してそれぞれひとつの属性を持っています。この属性は、文法で定義された型となります。たとえば
ast.BinOp
のインスタンスはleft
という属性を持っており、その型はast.expr
です。これらの属性が、文法上 (クエスチョンマークを用いて) オプションであるとマークされている場合は、その値が
None
となることもあります。属性が0個以上の複数の値をとりうる場合 (アスタリスクでマークされている場合) は、値は Python のリストで表されます。全ての属性は AST をcompile()
でコンパイルする際には存在しなければならず、そして妥当な値でなければなりません。
-
lineno
¶ -
col_offset
¶ ast.expr
やast.stmt
のサブクラスのインスタンスにはさらにlineno
やcol_offset
といった属性があります。lineno
はソーステキスト上の行番号 (1 から数え始めるので、最初の行の行番号は 1 となります)、そしてcol_offset
はノードが生成した最初のトークンの UTF-8 バイトオフセットとなります。 UTF-8 オフセットが記録される理由は、パーサが内部で UTF-8 を使用するからです。
クラス
ast.T
のコンストラクタは引数を次のように解析します:位置による引数があるとすれば、
T._fields
にあるのと同じだけの個数が無ければなりません。これらの引数はそこにある名前を持った属性として割り当てられます。キーワード引数があるとすれば、それらはその名前の属性にその値を割り当てられます。
たとえば、
ast.UnaryOp
ノードを生成して属性を埋めるには、次のようにすることができますnode = ast.UnaryOp() node.op = ast.USub() node.operand = ast.Num() node.operand.n = 5 node.operand.lineno = 0 node.operand.col_offset = 0 node.lineno = 0 node.col_offset = 0
もしくはよりコンパクトにも書けます
node = ast.UnaryOp(ast.USub(), ast.Num(5, lineno=0, col_offset=0), lineno=0, col_offset=0)
-
32.2.2. 抽象文法 (Abstract Grammar)¶
抽象文法は、現在次のように定義されています:
-- ASDL's six builtin types are identifier, int, string, bytes, object, singleton
module Python
{
mod = Module(stmt* body)
| Interactive(stmt* body)
| Expression(expr body)
-- not really an actual node but useful in Jython's typesystem.
| Suite(stmt* body)
stmt = FunctionDef(identifier name, arguments args,
stmt* body, expr* decorator_list, expr? returns)
| AsyncFunctionDef(identifier name, arguments args,
stmt* body, expr* decorator_list, expr? returns)
| ClassDef(identifier name,
expr* bases,
keyword* keywords,
stmt* body,
expr* decorator_list)
| Return(expr? value)
| Delete(expr* targets)
| Assign(expr* targets, expr value)
| AugAssign(expr target, operator op, expr value)
-- use 'orelse' because else is a keyword in target languages
| For(expr target, expr iter, stmt* body, stmt* orelse)
| AsyncFor(expr target, expr iter, stmt* body, stmt* orelse)
| While(expr test, stmt* body, stmt* orelse)
| If(expr test, stmt* body, stmt* orelse)
| With(withitem* items, stmt* body)
| AsyncWith(withitem* items, stmt* body)
| Raise(expr? exc, expr? cause)
| Try(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)
| Assert(expr test, expr? msg)
| Import(alias* names)
| ImportFrom(identifier? module, alias* names, int? level)
| Global(identifier* names)
| Nonlocal(identifier* names)
| Expr(expr value)
| Pass | Break | Continue
-- XXX Jython will be different
-- col_offset is the byte offset in the utf8 string the parser uses
attributes (int lineno, int col_offset)
-- BoolOp() can use left & right?
expr = BoolOp(boolop op, expr* values)
| BinOp(expr left, operator op, expr right)
| UnaryOp(unaryop op, expr operand)
| Lambda(arguments args, expr body)
| IfExp(expr test, expr body, expr orelse)
| Dict(expr* keys, expr* values)
| Set(expr* elts)
| ListComp(expr elt, comprehension* generators)
| SetComp(expr elt, comprehension* generators)
| DictComp(expr key, expr value, comprehension* generators)
| GeneratorExp(expr elt, comprehension* generators)
-- the grammar constrains where yield expressions can occur
| Await(expr value)
| Yield(expr? value)
| YieldFrom(expr value)
-- need sequences for compare to distinguish between
-- x < 4 < 3 and (x < 4) < 3
| Compare(expr left, cmpop* ops, expr* comparators)
| Call(expr func, expr* args, keyword* keywords)
| Num(object n) -- a number as a PyObject.
| Str(string s) -- need to specify raw, unicode, etc?
| Bytes(bytes s)
| NameConstant(singleton value)
| Ellipsis
-- the following expression can appear in assignment context
| Attribute(expr value, identifier attr, expr_context ctx)
| Subscript(expr value, slice slice, expr_context ctx)
| Starred(expr value, expr_context ctx)
| Name(identifier id, expr_context ctx)
| List(expr* elts, expr_context ctx)
| Tuple(expr* elts, expr_context ctx)
-- col_offset is the byte offset in the utf8 string the parser uses
attributes (int lineno, int col_offset)
expr_context = Load | Store | Del | AugLoad | AugStore | Param
slice = Slice(expr? lower, expr? upper, expr? step)
| ExtSlice(slice* dims)
| Index(expr value)
boolop = And | Or
operator = Add | Sub | Mult | MatMult | Div | Mod | Pow | LShift
| RShift | BitOr | BitXor | BitAnd | FloorDiv
unaryop = Invert | Not | UAdd | USub
cmpop = Eq | NotEq | Lt | LtE | Gt | GtE | Is | IsNot | In | NotIn
comprehension = (expr target, expr iter, expr* ifs)
excepthandler = ExceptHandler(expr? type, identifier? name, stmt* body)
attributes (int lineno, int col_offset)
arguments = (arg* args, arg? vararg, arg* kwonlyargs, expr* kw_defaults,
arg? kwarg, expr* defaults)
arg = (identifier arg, expr? annotation)
attributes (int lineno, int col_offset)
-- keyword arguments supplied to call (NULL identifier for **kwargs)
keyword = (identifier? arg, expr value)
-- import name with optional 'as' alias.
alias = (identifier name, identifier? asname)
withitem = (expr context_expr, expr? optional_vars)
}
32.2.3. ast
ヘルパー¶
ノード・クラスの他に、 ast
モジュールは以下のような抽象構文木をトラバースするためのユーティリティ関数やクラスも定義しています:
-
ast.
parse
(source, filename='<unknown>', mode='exec')¶ source を解析して AST ノードにします。
compile(source, filename, mode, ast.PyCF_ONLY_AST)
と等価です。
-
ast.
literal_eval
(node_or_string)¶ 式ノードまたは Python のリテラルまたはコンテナのディスプレイ表現を表す文字列を安全に評価します。与えられる文字列またはノードは次のリテラルのみからなるものに限られます: 文字列、バイト列、数、タプル、リスト、辞書、集合、ブール値、
None
。この関数は Python の式を含んだ信頼出来ない出どころからの文字列を、値自身を解析することなしに安全に評価するのに使えます。この関数は、例えば演算や添え字を含んだ任意の複雑な表現を評価するのには使えません。
バージョン 3.2 で変更: バイト列リテラルと集合リテラルが受け取れるようになりました。
-
ast.
get_docstring
(node, clean=True)¶ 与えられた node (これは
FunctionDef
,ClassDef
,Module
のいずれかのノードでなければなりません) のドキュメント文字列を返します。もしドキュメント文字列が無ければNone
を返します。 clean が真ならば、ドキュメント文字列のインデントをinspect.cleandoc()
を用いて一掃します。
-
ast.
fix_missing_locations
(node)¶ compile()
はノード・ツリーをコンパイルする際、lineno
とcol_offset
両属性をサポートする全てのノードに対しそれが存在するものと想定します。生成されたノードに対しこれらを埋めて回るのはどちらかというと退屈な作業なので、このヘルパーが再帰的に二つの属性がセットされていないものに親ノードと同じ値をセットしていきます。再帰の出発点が node です。
-
ast.
increment_lineno
(node, n=1)¶ node から始まるツリーの全てのノードの行番号を n ずつ増やします。これはファイルの中で別の場所に「コードを動かす」ときに便利です。
-
ast.
copy_location
(new_node, old_node)¶ ソースの場所 (
lineno
とcol_offset
) を old_node から new_node に可能ならばコピーし、 new_node を返します。
-
ast.
iter_fields
(node)¶ node にある
node._fields
のそれぞれのフィールドを(フィールド名, 値)
のタプルとして yield します。
-
ast.
iter_child_nodes
(node)¶ node の直接の子ノード全てを yield します。すなわち、yield されるのは、ノードであるような全てのフィールドおよびノードのリストであるようなフィールドの全てのアイテムです。
-
ast.
walk
(node)¶ node の全ての子孫ノード(node 自体を含む)を再帰的に yield します。順番は決められていません。この関数はノードをその場で変更するだけで文脈を気にしないような場合に便利です。
-
class
ast.
NodeVisitor
¶ 抽象構文木を渡り歩いてビジター関数を見つけたノードごとに呼び出すノード・ビジターの基底クラスです。この関数は
visit()
メソッドに送られる値を返してもかまいません。このクラスはビジター・メソッドを付け加えたサブクラスを派生させることを意図しています。
-
visit
(node)¶ ノードを訪れます。デフォルトの実装では
self.visit_classname
というメソッド (ここで classname はノードのクラス名です) を呼び出すか、そのメソッドがなければgeneric_visit()
を呼び出します。
-
generic_visit
(node)¶ このビジターはノードの全ての子について
visit()
を呼び出します。注意して欲しいのは、専用のビジター・メソッドを具えたノードの子ノードは、このビジターが
generic_visit()
を呼び出すかそれ自身で子ノードを訪れない限り訪れられないということです。
トラバースの途中でノードを変化させたいならば
NodeVisitor
を使ってはいけません。そうした目的のために変更を許す特別なビジター (NodeTransformer
) があります。-
-
class
ast.
NodeTransformer
¶ NodeVisitor
のサブクラスで抽象構文木を渡り歩きながらノードを変更することを許すものです。NodeTransformer
は抽象構文木(AST)を渡り歩き、ビジター・メソッドの戻り値を使って古いノードを置き換えたり削除したりします。ビジター・メソッドの戻り値がNone
ならば、ノードはその場から取り去られ、そうでなければ戻り値で置き換えられます。置き換えない場合は戻り値が元のノードそのものであってもかまいません。それでは例を示しましょう。Name (たとえば
foo
) を見つけるたび全てdata['foo']
に書き換える変換器 (transformer) です:class RewriteName(NodeTransformer): def visit_Name(self, node): return copy_location(Subscript( value=Name(id='data', ctx=Load()), slice=Index(value=Str(s=node.id)), ctx=node.ctx ), node)
操作しようとしているノードが子ノードを持つならば、その子ノードの変形も自分で行うか、またはそのノードに対し最初に
generic_visit()
メソッドを呼び出すか、それを行うのはあなたの責任だということを肝に銘じましょう。文のコレクションであるようなノード (全ての文のノードが当てはまります) に対して、このビジターは単独のノードではなくノードのリストを返すかもしれません。
たいてい、変換器の使い方は次のようになります:
node = YourTransformer().visit(node)
-
ast.
dump
(node, annotate_fields=True, include_attributes=False)¶ node 中のツリーのフォーマットされたダンプを返します。主な使い道はデバッグです。返される文字列は名前とフィールドの値を表示します。これを使うとコードは評価できなくなりますので、評価が必要ならば annotate_fields に
False
をセットしなければなりません。行番号や列オフセットのような属性はデフォルトではダンプされません。これが欲しければ、include_attributes をTrue
にセットすることができます。
参考
外部ドキュメント Green Tree Snakes には Python AST についての詳細が書かれています。