バージョン 2.5 で追加: 低級モジュール _ast はノード・クラスだけ含みます。
バージョン 2.6 で追加: 高級モジュール ast は全てのヘルパーを含みます。
ast モジュールは、Python アプリケーションで Python の抽象構文木を処理しやすくするものです。 抽象構文そのものは、Python のリリースごとに変化する可能性があります。 このモジュールを使用すると、現在の文法をプログラム上で知る助けになるでしょう。
抽象構文木を作成するには、 ast.PyCF_ONLY_AST を組み込み関数 compile() のフラグとして渡すか、あるいはこのモジュールで提供されているヘルパー関数 parse() を使います。 その結果は、 ast.AST を継承したクラスのオブジェクトのツリーとなります。 抽象構文木は組み込み関数 compile() を使って Python コード・オブジェクトにコンパイルすることができます。
このクラスは全ての AST ノード・クラスの基底です。 実際のノード・クラスは 後ほど 示す Parser/Python.asdl ファイルから派生したものです。 これらのクラスは _ast C モジュールで定義され、 ast にもエクスポートし直されています。
抽象文法の左辺のシンボル一つずつにそれぞれ一つのクラスがあります (たとえば ast.stmt や ast.expr)。 それに加えて、右辺のコンストラクタ一つずつにそれぞれ一つのクラスがあり、 これらのクラスは左辺のツリーのクラスを継承しています。 たとえば、 BinOp は ast.expr から継承しています。 代替を伴った生成規則 (production rules with alternatives) (別名 “sums”) の場合、左辺は抽象クラスとなり、 特定のコンストラクタ・ノードのインスタンスのみが作成されます。
各具象クラスは属性 _fields を持っており、すべての子ノードの名前をそこに保持しています。
具象クラスのインスタンスは、各子ノードに対してそれぞれひとつの属性を持っています。 この属性は、文法で定義された型となります。 たとえば ast.BinOp のインスタンスは left という属性を持っており、その型は ast.expr です。
これらの属性が、文法上 (クエスチョンマークを用いて) オプションであるとマークされている場合は、その値が None となることもあります。 属性が0個以上の複数の値をとりうる場合 (アスタリスクでマークされている場合) は、 値は Python のリストで表されます。 全ての属性は AST を compile() でコンパイルする際には存在しなければならず、 そして妥当な値でなければなりません。
ast.expr や ast.stmt のサブクラスのインスタンスにはさらに lineno や col_offset といった属性があります。 lineno はソーステキスト上の行番号 (1 から数え始めるので、最初の行の行番号は 1 となります)、そして col_offset はノードが生成した最初のトークンの UTF-8 バイトオフセットとなります。 UTF-8 オフセットが記録される理由は、パーサが内部で UTF-8 を使用するからです。
クラス ast.T のコンストラクタは引数を次のように解析します:
たとえば、 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)
バージョン 2.6 で追加: 上で説明したコンストラクタが付け加えられました。 Python 2.5 においてはノードは引数なしのコンストラクタを呼び出して生成した後、 全ての属性をセットしていかなければなりませんでした。
このモジュールでは文字列定数 __version__ を定義しています。これは、以下に示すファイルの Subversion リビジョン番号です。
抽象文法は、現在次のように定義されています。
-- ASDL's five builtin types are identifier, int, string, object, bool
module Python version "$Revision: 62047 $"
{
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)
| ClassDef(identifier name, expr* bases, stmt* body, expr *decorator_list)
| Return(expr? value)
| Delete(expr* targets)
| Assign(expr* targets, expr value)
| AugAssign(expr target, operator op, expr value)
-- not sure if bool is allowed, can always use int
| Print(expr? dest, expr* values, bool nl)
-- use 'orelse' because else is a keyword in target languages
| For(expr target, expr iter, stmt* body, stmt* orelse)
| While(expr test, stmt* body, stmt* orelse)
| If(expr test, stmt* body, stmt* orelse)
| With(expr context_expr, expr? optional_vars, stmt* body)
-- 'type' is a bad name
| Raise(expr? type, expr? inst, expr? tback)
| TryExcept(stmt* body, excepthandler* handlers, stmt* orelse)
| TryFinally(stmt* body, stmt* finalbody)
| Assert(expr test, expr? msg)
| Import(alias* names)
| ImportFrom(identifier module, alias* names, int? level)
-- Doesn't capture requirement that locals must be
-- defined if globals is
-- still supports use as a function!
| Exec(expr body, expr? globals, expr? locals)
| Global(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)
| ListComp(expr elt, comprehension* generators)
| GeneratorExp(expr elt, comprehension* generators)
-- the grammar constrains where yield expressions can occur
| Yield(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,
expr? starargs, expr? kwargs)
| Repr(expr value)
| Num(object n) -- a number as a PyObject.
| Str(string s) -- need to specify raw, unicode, etc?
-- other literals? bools?
-- the following expression can appear in assignment context
| Attribute(expr value, identifier attr, expr_context ctx)
| Subscript(expr value, slice slice, 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 = Ellipsis | Slice(expr? lower, expr? upper, expr? step)
| ExtSlice(slice* dims)
| Index(expr value)
boolop = And | Or
operator = Add | Sub | Mult | 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)
-- not sure what to call the first argument for raise and except
excepthandler = ExceptHandler(expr? type, expr? name, stmt* body)
attributes (int lineno, int col_offset)
arguments = (expr* args, identifier? vararg,
identifier? kwarg, expr* defaults)
-- keyword arguments supplied to call
keyword = (identifier arg, expr value)
-- import name with optional 'as' alias.
alias = (identifier name, identifier? asname)
}
バージョン 2.6 で追加.
ノード・クラスの他に、 ast モジュールは以下のような抽象構文木をトラバースするためのユーティリティ関数やクラスも定義しています。
式を解析して AST ノードにします。 compile(expr, filename, mode, ast.PyCF_ONLY_AST) と等価です。
式ノードまたは Python の式を表す文字列を安全に評価します。 与えられる文字列またはノードは次のリテラルのみからなるものに限られます: 文字列, 数, タプル, リスト, 辞書, ブール値, None 。
この関数は Python の式を含んだ必ずしも信用できない出どころからの文字列を、 自身での値の解析を要さずに、安全に評価するのに使えます。
与えられた node (これは FunctionDef, ClassDef, Module のいずれかのノードでなければなりません) のドキュメント文字列を返します。 もしドキュメント文字列が無ければ None を返します。 clean が真ならば、ドキュメント文字列のインデントを inspect.cleandoc() を用いて一掃します。
compile() はノード・ツリーをコンパイルする際、 lineno と col_offset 両属性をサポートする全てのノードに対しそれが存在するものと想定します。 生成されたノードに対しこれらを埋めて回るのはどちらかというと退屈な作業なので、 このヘルパーが再帰的に二つの属性がセットされていないものに親ノードと同じ値をセットしていきます。 再帰の出発点が node です。
node から始まるツリーの全てのノードの行番号を n ずつ増やします。 これはファイルの中で別の場所に「コードを動かす」ときに便利です。
ソースの場所 (lineno と col_offset) を old_node から new_node に可能ならばコピーし、 new_node を返します。
node にある node._fields のそれぞれのフィールドを (フィールド名, 値) のタプルとして yield します。
node の直接の子ノード全てを yield します。 すなわち、 yield されるのは、ノードであるような全てのフィールドおよびノードのリストであるようなフィールドの全てのアイテムです。
node の全ての子ノードを再帰的に yield します。 順番は決められていません。 この関数はノードをその場で変更するだけで文脈を気にしないような場合に便利です。
抽象構文木を渡り歩いてビジター関数を見つけたノードごとに呼び出すノード・ビジターの基底クラスです。 この関数は visit() メソッドに送られる値を返してもかまいません。
このクラスはビジター・メソッドを付け加えたサブクラスを派生させることを意図しています。
- visit(node)¶
ノードを訪れます。 デフォルトの実装では self.visit_classname というメソッド (ここで classname はノードのクラス名です) を呼び出すか、 そのメソッドがなければ generic_visit() を呼び出します。
- generic_visit(node)¶
このビジターはノードの全ての子について visit() を呼び出します。
注意して欲しいのは、専用のビジター・メソッドを具えたノードの子ノードは、 このビジターが generic_visit() を呼び出すかそれ自身で子ノードを訪れない限り訪れられないということです。
トラバースの途中でノードを変化させたいならば NodeVisitor を使ってはいけません。 そうした目的のために変更を許す特別なビジター (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)
node 中のツリーのフォーマットされたダンプを返します。 主な使い道はデバッグです。 返される文字列は名前とフィールドの値を表示します。 これを使うとコードは評価できなくなりますので、 評価が必要ならば annotated_fields に False をセットしなければなりません。 行番号や列オフセットのような属性はデフォルトではダンプされません。 これが欲しければ、 include_attributes を True にセットすることができます。