Python が動くまで
書いた文字が、コンピュータの実行できる形になるまでを順に追います。 題材は短いプログラムひとつです。
こういうプログラムがあります。
n = 2 total = 3 + 4 * n
* はかけ算を表します。
= は等号ではなく、右側を計算して左の名前にしまえ、という指示です。
かけ算が先ですから、total には 11 が入ります。
人が読めば、これだけのことがわかります。
切り分けたもの
total = 3 + 4 * n は、人が見れば単語の集まりです。 しかし文字としては、こう並んでいるだけです。
どこが一語なのかは、どこにも書かれていません。
組み合わせたもの
切り分けても、まだ計算はできません。
3 + 4 * n を、二とおりの順番で計算してみてください。
並べ直したもの
組み合わせが決まっても、まだ足りません。
木が表しているのは、何と何が組かということだけです。 どこから手をつけるかは、どこにも書かれていません。
木のまま実行することもできますが、Python は速さのために、先に一列へ並べ直します。
ほどき方は、左の枝から先にたどります。
値は出会った順に出し、演算は両方の枝が済んだところで出します。
文が二つあるときは、書かれた順に並べます。
しまう だけは、右の枝から片付けます。 しまうものが手元に無ければ、しまえないからです。
そして左の枝の total は、値として出されることがありません。total にしまう という一つの命令の中に、しまい先の名前として入ります。
LOAD_CONST22 を出すSTORE_NAMEnn にしまうLOAD_CONST33 を出すLOAD_CONST44 を出すLOAD_NAMEnn の中身を出すBINARY_OP*かけるBINARY_OP+たすSTORE_NAMEtotaltotal にしまう
先頭の二つは、一行目の n = 2 から出たものです。 三つめの 3 は、+ の左の枝なので、4 * n より先に出されます。
出したものは、どこに出すのか
ここで、ひとつ引っかかるところがあります。
「3 を出す」と言われて、どこに出すのでしょうか。
Python は、値を積み上げる場所を持っています。 出したものはそこに積み、使うときは一番上から取ります。
まだ何も積まれていません
読んでいるのは、誰か
命令の列を上から読んで、一つずつ処理していく別のプログラムがあります。CPython といいます。
Python は言語の名前で、それを実際に動かすのがこの CPython です。 ここまで Python がやっていると書いてきたことは、正確にはこの CPython がやっています。
最初に見せた十一行は、その CPython の一部です。
+ を見つけたときに動き出す部分の、いちばん先頭にあたります。
そのうち五行は push という命令で、後で使うために、いま入っている値を 退避しているだけです。まだ何も始めていません。
始まっているのは、この一行からです。
渡された値から、その値の種類を取り出しています。 続く三行で、その種類が足し算のやり方を持っているかどうかを調べます。
まだ足していません。
足す相手が、どういう種類のものなのかを見ているところです。
なぜ、そんなことを調べるのか。+ は、足し算とは限らないからです。
'abc' + 'def' → 'abcdef'
' で囲んだものは、計算されずそのままの文字として扱われます。 文字どうしを + でつなぐと、くっついて一つになります。 同じ + でも、数を足すのか、文字をつなぐのかで、やることがまるで違う。
では、どちらなのかを先に決めておけばよさそうなものです。ところが、決められません。
切り分けの段階では、字面しか見ていませんでした。
木を組む段階でも、n の中身は見ていません。
並べ直した BINARY_OP + という命令にも、何を足すのかは書かれていません。
n に何が入るかは、実行してみるまで決まらない。 だから CPython は、足すその瞬間に、自分で調べるしかありません。
十一命令かけて、まだ足していない。
その大半が、これから何をするか決めるための下ごしらえです。
同じ名前でも、止まった場所は違う
書いたものが受け付けられないとき、三つのどこかで止まっています。
total = 3 + 4 *
File "e1.py", line 1
total = 3 + 4 *
^
SyntaxError: invalid syntax最後の行は「構文エラー。組み立てられません」という意味です。組み立てで止まった
切り分けは成功しています。* の右側に何も無いので、組を作れませんでした。^ の印が指しているのは、まさにその場所です。
total = 'abc
File "e2.py", line 1
total = 'abc
^
SyntaxError: unterminated string literal (detected at line 1)最後の行は「終わっていない文字列」という意味です。切り分けで止まった
一番長いかたまりを取ろうとして、閉じる ' を探しに行き、行の終わりまで見つかりませんでした。どこまでが一つのかたまりなのかが決まらないので、切り分けが完了しません。^ の印は、探し始めた場所を指しています。
どちらも SyntaxError ですが、文面が違います。 片方は組み立ての話をし、もう片方はかたまりの話をしています。 止まった場所が違えば、直す場所も違います。
n = 2 total = 3 + 4 * n
この二行は、最後まで機械語になりません。
機械語になっていたのは、これを読む側でした。
ここに出てきたトークン・構文木・命令の列は、本物の CPython(3.12.11)が出したものです。 積み上げの様子だけは、命令の列から組み立てて描いています。
機械語は Linux 版の CPython から取り出しました。 このページ自体はブラウザの中で動いているので、そちらでは別の形に翻訳された CPython が動いています。
画面を短くするため、行の終わりや始まりを示す印は図から省いてあります。 省かずに全部見たいときは、プレイグラウンドで確かめられます。