Pythonバイトコードの逆アセンブル:disモジュールの使い方徹底解説
Python標準ライブラリのdisモジュールは、Pythonバイトコードを人間が読みやすい形式へ逆アセンブルし、解析するための多彩な関数を提供しています。バイトコードを解析することで、プログラムの最適化を効率的に行えます。なお、バイトコードはインタープリタのバージョンごとに異なる実装の詳細である点に注意してください。
dis()関数
dis()関数は、モジュール、クラス、メソッド、関数、コードオブジェクトなど、あらゆるPythonコードソースの逆アセンブル結果を生成します。
>>> def hello():
print("hello world")
>>> import dis
>>> dis.dis(hello)
2 0 LOAD_GLOBAL 0 (print)
3 LOAD_CONST 1 ('hello world')
6 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
9 POP_TOP
10 LOAD_CONST 0 (None)
13 RETURN_VALUE
Bytecode()クラス
バイトコード解析用のAPIはBytecodeクラスとして定義されています。コンストラクタはBytecodeオブジェクトを返し、このオブジェクトにはバイトコードを解析するためのさまざまな機能が備わっています。
Bytecode()はコンストラクタであり、関数、ジェネレータ、メソッド、ソースコード文字列、またはコードオブジェクトに対応するバイトコードを解析します。複数の関数をまとめて扱える便利なラッパーとして機能します。
>>> string = dis.Bytecode(hello)
>>> for x in string:
print(x)
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='print', argrepr='print', offset=0, starts_line=2, is_jump_target=False)
Instruction(opname='LOAD_CONST', opcode=100, arg=1, argval='hello world', argrepr="'hello world'", offset=3, starts_line=None, is_jump_target=False)
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=6, starts_line=None, is_jump_target=False)
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=9, starts_line=None, is_jump_target=False)
Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=10, starts_line=None, is_jump_target=False)
Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=13, starts_line=None, is_jump_target=False)
code_info()関数
この関数は、Pythonコードオブジェクトに関する情報を返します。
>>> dis.code_info(hello) "Name: hello\nFilename: <pyshell#2>\nArgument count: 0\nKw-only arguments: 0\nNumber of locals: 0\nStack size: 2\nFlags: OPTIMIZED, NEWLOCALS, NOFREE\nConstants:\n 0: None\n 1: 'hello world'\nNames:\n 0: print"
show_code()関数
この関数は、Pythonのモジュール、関数、またはクラスに関する詳細なコード情報を出力します。
>>> dis.show_code(hello)
Name: hello
Filename: <pyshell#2>
Argument count: 0
Kw-only arguments: 0
Number of locals: 0
Stack size: 2
Flags: OPTIMIZED, NEWLOCALS, NOFREE
Constants:
0: None
1: 'hello world'
Names:
0: print
disassemble()関数
この関数はコードオブジェクトを逆アセンブルし、以下の列に分けて出力します。
- 各行の最初の命令に対応する行番号
- 現在実行中の命令(-->で表示)
- ジャンプ先となる命令(>>で表示)
- 命令のアドレス
- オペレーションコード名
- 操作パラメータ
- 括弧内に示されるパラメータの解釈
>>> codeInString = 'a = 5\nb = 6\nsum = a + b \nprint("sum =",sum)'
>>> codeObject = compile(codeInString, 'sumstring', 'exec')
>>> dis.disassemble(codeObject)
出力例
1 0 LOAD_CONST 0 (5)
3 STORE_NAME 0 (a)
2 6 LOAD_CONST 1 (6)
9 STORE_NAME 1 (b)
3 12 LOAD_NAME 0 (a)
15 LOAD_NAME 1 (b)
18 BINARY_ADD
19 STORE_NAME 2 (sum)
4 22 LOAD_NAME 3 (print)
25 LOAD_CONST 2 ('sum =')
28 LOAD_NAME 2 (sum)
31 CALL_FUNCTION 2 (2 positional, 0 keyword pair)
34 POP_TOP
35 LOAD_CONST 3 (None)
38 RETURN_VALUE
get_instructions()関数
この関数は、指定された関数、メソッド、ソースコード文字列、またはコードオブジェクト内の命令を順に走査するイテレータを返します。イテレータは、コード内の各操作の詳細を格納したInstructionという名前付きタプル(namedtuple)の系列を生成します。
>>> it = dis.get_instructions(code)
>>> for i in it:
print(i)
Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=<code object hello at 0x02A9BA70, file "<disassembly>", line 2>, argrepr='<code object hello at 0x02A9BA70, file "<disassembly>", line 2>', offset=0, starts_line=2, is_jump_target=False)
Instruction(opname='LOAD_CONST', opcode=100, arg=1, argval='hello', argrepr="'hello'", offset=3, starts_line=None, is_jump_target=False)
Instruction(opname='MAKE_FUNCTION', opcode=132, arg=0, argval=0, argrepr='', offset=6, starts_line=None, is_jump_target=False)
Instruction(opname='STORE_NAME', opcode=90, arg=0, argval='hello', argrepr='hello', offset=9, starts_line=None, is_jump_target=False)
Instruction(opname='LOAD_CONST', opcode=100, arg=2, argval=None, argrepr='None', offset=12, starts_line=None, is_jump_target=False)
Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=15, starts_line=None, is_jump_target=False)
各命令の情報は、以下のパラメータを持つタプル形式のオブジェクトとして提供されます。
| フィールド名 | バイトコード操作の詳細 |
|---|---|
| opcode | 操作に対応する数値コード。Opcodeコレクションに列挙されているopcode値およびバイトコード値に対応します。 |
| opname | 操作を表す人間が読める名前 |
| arg | 操作への数値引数(存在する場合)。なければNone |
| argval | 解決済みの引数値(既知の場合)。不明な場合はargと同じ値 |
| argrepr | 操作引数の人間が読める説明 |
| offset | バイトコードシーケンス内における操作の開始インデックス |
| starts_line | このopcodeが開始する行番号(存在する場合)。なければNone |
| is_jump_target | 他のコードからここへジャンプしてくる場合はTrue、それ以外はFalse |
なお、バイトコードの内容やopcode番号はPythonのバージョンによって異なるため、実際の出力は使用しているインタープリタのバージョンに依存することに留意してください。
-
Windowsで使えるPython開発用IDEおすすめ7選|PyCharmからAtomまで徹底解説
Windows環境でPythonの開発を始めるとき、最初に悩むのが「どのIDE(統合開発環境)を選ぶべきか」という問題です。IDEはコードの編集・実行・デバッグなどをひとつのアプリケーションで完結できる強力なツールであり、選択次第で開発効率が大きく変わります。 本記事では、Windowsで利用できる代表的なPython対応IDEを厳選し、それぞれの特徴やメリットをわかりやすく解説します。 PyCharm PyCharmはJetBrains社が開発する、Python開発で最も人気のあるIDEのひとつです。無料のコミュニティ版と有料のプロフェッショナル版が用意されており、初心者からプロフェッショ
-
Pythonコードを高速化する11の最適化テクニック
Pythonはコンパイル言語ほど高速ではないことは周知の事実ですが、大手企業がPythonで大規模なワークロードを処理している事例からも分かるように、決して「遅い言語」ではありません。この記事では、正しく動作するPythonプログラムをさらに速く、効率的にするために押さえておきたい最適化のヒントを11個紹介します。 ヒント1: 組み込み関数を活用する Pythonで独自に効率的なコードを書くことも可能ですが、C言語で実装された組み込み関数の速度には到底及びません。利用可能な組み込み関数はdir(__builtins__)で一覧表示できるので、車輪の再発明をする前に必ず確認しましょう。 ヒン