Pythonのバイトコードは、ソースコードがコンパイルされた後の中間表現であり、CPythonの仮想機械(PVM)によって実行されます。このバイトコードは通常.pycファイルとしてバイナリ形式で保存されますが、Python標準ライブラリのdisモジュールを用いることで、可読性の高いテキスト形式の命令群(アセンブラに似た mnemonics)に変換可能です。
以下に、関数のバイトコードを表示する基本的な使用例とその出力を示します。
import dis
def multiply(x, y):
return x * y
dis.dis(multiply)
出力例(Python 3.11環境):
2 0 LOAD_FAST 0 (x)
2 LOAD_FAST 1 (y)
4 BINARY_MULTIPLY
6 RETURN_VALUE
| 列の位置 | 意味 |
|---|---|
2 |
対応するソースコードの行番号 |
0, 2, 4, 6 |
バイトコード命令の先頭オフセット(バイト単位) |
LOAD_FAST, BINARY_MULTIPLY, RETURN_VALUE |
実行される命令とその動作 |
より実践的な例として、文字列の連結と関数呼び出しを含む関数のバイトコードを確認します。
import dis
def format_message(user, greeting="Hi"):
text = f"{greeting}, {user}!"
print(text)
dis.dis(format_message)
出力(一部省略):
2 0 LOAD_FAST 1 (greeting)
2 LOAD_FAST 0 (user)
4 BUILD_STRING 3
6 STORE_FAST 2 (text)
3 8 LOAD_GLOBAL 0 (print)
10 LOAD_FAST 2 (text)
12 CALL_FUNCTION 1
14 POP_TOP
16 LOAD_CONST 0 (None)
18 RETURN_VALUE
主な命令の意味:
BUILD_STRING n:n個の堆積された文字列オブジェクトを結合し、新しい文字列オブジェクトを生成STORE_FAST:ローカル変数スタックの先頭値をローカル変数に保存CALL_FUNCTION n:直前にロードされたオブジェクト(関数)を引数n個で呼び出しPOP_TOP:スタック上のトップ値を破棄(戻り値が不要な場合)RETURN_VALUE:スタックトップの値を関数の戻り値として返却
※注: BINARY_ADDなど一部の命令は、Python 3.11以降ではBINARY_OP命令に統合され、操作種別が引数として与えられるよう変更されています。上記は比較的新しいバージョンに合わせた表現例です。
バイトコードは、CPythonの実装に依存しており、Pythonのバージョンごとに命令セットやフォーマットに違いがあります。そのため、異なるバージョン間でのバイナリ互換性は保証されません。また、.pycファイルは__pycache__ディレクトリ以下に保存され、ファイル名にはPythonのバージョン番号(例: cpython-311)が含まれます。
手動でコードオブジェクトを生成し、バイトコードを検査することも可能です。
import dis
src = "total = 10 + value"
code_obj = compile(src, "<dynamic>", "exec")
dis.dis(code_obj)
出力例:
1 0 LOAD_CONST 0 (10)
2 LOAD_NAME 0 (value)
4 BINARY_OP 0 (+)
6 STORE_NAME 1 (total)
8 LOAD_CONST 1 (None)
10 RETURN_VALUE
このように、disモジュールを用いることで、Pythonプログラムの内部動作を直接観察・分析することが可能であり、パフォーマンス最適化やデバッグ、言語実装の理解に非常に有用です。