printデバッグから卒業して、効率的なデバッグ手法を身につけよう
Pythonで開発を進めていると、予期せぬエラーやバグに遭遇することは避けられません。そんな時、多くの開発者がまず試すのが print()
関数を使ったデバッグ、いわゆる「printデバッグ」ではないでしょうか? 🤔 変数の値を表示させてコードの挙動を確認するこの方法は手軽ですが、複雑なプログラムになると、print()
を大量に仕込む必要が出てきたり、どの print()
がどの場所に対応するのかわからなくなったりと、効率が悪くなることがあります。
そこで登場するのが、Pythonのデバッグツールです。Pythonには標準で pdb
(The Python Debugger) というデバッガが用意されていますが、さらに高機能で使いやすいデバッガとして人気なのが、今回ご紹介する ipdb
です! ✨
ipdb
は、標準の pdb
の機能に加えて、強力な対話型シェルである IPython
の機能を統合したデバッガです。これにより、コード補完、シンタックスハイライト、マジックコマンドなど、IPython
の便利な機能をデバッグ中に利用できるようになります。
この記事では、ipdb
の基本的な使い方から、便利なコマンド、設定方法まで、詳しく解説していきます。これを読めば、あなたも ipdb
を使いこなし、デバッグ作業を格段に効率化できるはずです! 💪
ipdb とは? pdb との違い
前述の通り、ipdb
は IPython
を利用した Python デバッガです。標準ライブラリの pdb
をベースにしており、pdb
の基本的な機能はすべて利用可能です。
pdb
と比較した際の ipdb
の主なメリットは以下の通りです。
- タブ補完: 変数名やオブジェクトの属性などを入力する際に、Tabキーで候補を補完できます。タイプミスを防ぎ、素早くコードを入力できます。
- シンタックスハイライト: デバッガコンソール内のコードが見やすく色付けされます。コードの構造を把握しやすくなります。
- マジックコマンド:
%
から始まるIPython
のマジックコマンドを利用できます(一部)。 - より分かりやすいトレースバック表示: エラー発生時のトレースバック(エラー発生箇所までの関数呼び出し履歴)が色付きで表示され、問題箇所を特定しやすくなります。
- オブジェクト情報の詳細表示:
pinfo
やpinfo2
といったコマンドで、オブジェクトの詳細な情報を確認できます。
一方で、ipdb
は標準ライブラリではないため、利用するには別途インストールが必要です。
インストール
ipdb
は pip を使って簡単にインストールできます。ターミナル(コマンドプロンプト)で以下のコマンドを実行してください。
pip install ipdb
ipdb
は ipython
に依存しているため、ipython
がインストールされていない場合は、一緒にインストールされます。
基本的な使い方: ブレークポイントの設定
ipdb
を使う最も基本的な方法は、コードの実行を一時停止したい箇所(ブレークポイント)に以下のコードを挿入することです。
import ipdb; ipdb.set_trace()
Python 3.7以降では、標準で breakpoint()
という組み込み関数が導入されました。環境変数 PYTHONBREAKPOINT
を設定することで、breakpoint()
を呼び出した際に ipdb
を起動させることも可能です。これにより、コード内に import ipdb
を書く必要がなくなります。
export PYTHONBREAKPOINT=ipdb.set_trace
上記の環境変数を設定した後、コード内では以下のように記述します。
# import ipdb は不要
breakpoint() # ここで ipdb が起動する
では、簡単な例を見てみましょう。以下の sample.py
というファイルを作成します。
# sample.py
import ipdb
def calculate_average(numbers):
total = sum(numbers)
count = len(numbers)
# ここで処理を一時停止して変数を確認したい
ipdb.set_trace()
average = total / count
return average
data = [10, 20, 30, 40, 50]
avg = calculate_average(data)
print(f"平均値: {avg}")
このファイルをターミナルで実行します。
python sample.py
すると、ipdb.set_trace()
を記述した行の手前で処理が一時停止し、以下のような ipdb
のプロンプトが表示されます。
> /path/to/your/sample.py(9)calculate_average()
7 count = len(numbers)
8 # ここで処理を一時停止して変数を確認したい
----> 9 ipdb.set_trace()
10 average = total / count
11 return average
ipdb>
ipdb>
の後にコマンドを入力することで、デバッグ操作を行います。矢印 (---->
) は、次に実行される行を示しています。
ipdb の主要コマンド
ipdb
のプロンプトでは、様々なコマンドを使用してプログラムの実行を制御したり、状態を確認したりできます。多くのコマンドは pdb
と共通ですが、ipdb
独自のコマンドもあります。主要なコマンドをいくつか見ていきましょう。コマンドは短縮形(例: n
は next
)でも入力できます。
コマンド | 短縮形 | 説明 |
---|---|---|
next |
n |
現在の行を実行し、次の行へ進む。関数呼び出しがあっても関数の中には入らない(ステップオーバー)。 |
step |
s |
現在の行を実行し、次の実行可能な行へ進む。関数呼び出しがあればその関数の中に入る(ステップイン)。 |
continue |
c |
次のブレークポイントまで、またはプログラムが終了するまで実行を継続する。 |
quit |
q |
デバッガを終了し、プログラムの実行も中断する。 |
list |
l |
現在の行周辺のソースコードを表示する。 |
longlist |
ll |
現在の関数またはフレーム全体のソースコードを表示する。 |
print <expression> |
p <expression> |
式(変数名など)を評価し、その結果を表示する。 |
pp <expression> |
pp <expression> |
式を評価し、結果を整形して(pretty-print)表示する。大きな辞書やリストを見やすく表示するのに便利。 |
args |
a |
現在の関数の引数を表示する。 |
where |
w / bt |
現在のコールスタック(関数呼び出しの履歴)を表示する。エラー発生箇所や現在の実行位置の特定に役立つ。 |
up |
u |
コールスタックを一つ上のフレーム(呼び出し元の関数)に移動する。 |
down |
d |
コールスタックを一つ下のフレーム(呼び出された関数)に移動する。 |
return |
r |
現在の関数が値を返すまで実行を継続する。 |
jump <lineno> |
j <lineno> |
指定した行番号 (<lineno> ) に実行位置を移動する(注意して使用)。 |
help [<command>] |
h [<command>] |
利用可能なコマンドの一覧、または指定したコマンドのヘルプを表示する。 |
!<statement> |
!<statement> |
Pythonのステートメントを実行する。変数に値を代入したり、メソッドを呼び出したりできる。 |
pinfo <object> |
obj? |
オブジェクトの詳細情報を表示する(ipdb 独自)。 |
pinfo2 <object> |
obj?? |
オブジェクトのさらに詳細な情報(ソースコードなど)を表示する(ipdb 独自)。 |
psource <object> |
オブジェクトのソースコードを表示する(ipdb 独自)。 |
|
pdoc <object> |
オブジェクトのドキュメンテーション文字列(docstring)を表示する(ipdb 独自)。 |
|
pfile <object> |
オブジェクトが定義されているファイル名を表示する(ipdb 独自)。 |
|
interact |
現在のスコープで IPython の対話シェルを開始する。デバッグ中に複雑な操作を試したい場合に便利。終了するには `exit()` または `quit()` を入力。 |
先ほどの sample.py
の例で、いくつかコマンドを試してみましょう。
ipdb> p total # 変数 total の値を表示
150
ipdb> p count # 変数 count の値を表示
5
ipdb> a # 現在の関数の引数を表示
numbers = [10, 20, 30, 40, 50]
ipdb> ll # 現在の関数のソースコードを表示
4 def calculate_average(numbers):
5 total = sum(numbers)
6 count = len(numbers)
7 # ここで処理を一時停止して変数を確認したい
8 ipdb.set_trace()
9 -> average = total / count
10 return average
ipdb> n # 次の行へ進む
> /path/to/your/sample.py(10)calculate_average()
8 ipdb.set_trace()
9 average = total / count
---> 10 return average
11
12 data = [10, 20, 30, 40, 50]
ipdb> p average # 変数 average の値を確認
30.0
ipdb> c # 処理を最後まで続行
平均値: 30.0
c
がある状態で c
と入力するなど)、コマンドが優先されます。変数の値を表示したい場合は、p <変数名>
または !<変数名>
のように明示的に指定する必要があります。
高度な使い方
ipdb
は基本的なデバッグ操作以外にも、より高度な機能を提供します。
条件付きブレークポイント
特定の条件が満たされた場合にのみデバッガを起動させたい場合があります。例えば、ループ処理の中で特定の値になった時だけ停止したい場合などです。これには、set_trace()
の後に条件式を記述します。
import ipdb
data = range(10)
target_value = 7
for i in data:
print(f"Processing: {i}")
# i が target_value と一致した場合のみ停止
if i == target_value:
ipdb.set_trace()
# 何らかの処理...
print("Done")
また、ipdb
のコマンドラインから condition
コマンドを使って設定することも可能です。まず b <行番号>
でブレークポイントを設定し、次に condition <ブレークポイント番号> <条件式>
で条件を設定します。
ipdb> b 9 # 9行目にブレークポイントを設定 (例)
Breakpoint 1 at /path/to/your/script.py:9
ipdb> condition 1 i == target_value # ブレークポイント1に条件を設定
ipdb> c
事後デバッグ (Post-mortem debugging)
プログラムが予期せぬエラーでクラッシュした場合、エラーが発生した時点の状態を調査したいことがあります。これを事後デバッグと呼びます。ipdb
では、ipdb.pm()
または ipdb.post_mortem()
を使って事後デバッグを開始できます。
スクリプトを実行する際に python -m ipdb your_script.py
のように起動すると、スクリプトが例外で終了した場合に自動的に事後デバッグモードに入ります。
# エラーが発生するコード (div_zero.py)
def divide(a, b):
result = a / b # ここでゼロ除算エラーが発生する可能性がある
return result
x = 10
y = 0
print(divide(x, y))
python -m ipdb div_zero.py
上記を実行すると、ZeroDivisionError
が発生した箇所で ipdb
が起動し、エラー発生時の変数の値などを確認できます。
Traceback (most recent call last):
File "/usr/lib/python3.x/runpy.py", line 197, in _run_module_as_main
return _run_code(code, main_globals, None,
...
File "/path/to/your/div_zero.py", line 7, in <module>
print(divide(x, y))
File "/path/to/your/div_zero.py", line 3, in divide
result = a / b # ここでゼロ除算エラーが発生する可能性がある
ZeroDivisionError: division by zero
> /path/to/your/div_zero.py(3)divide()
1 def divide(a, b):
2 # ここでゼロ除算エラーが発生する可能性がある
----> 3 result = a / b
4 return result
5
ipdb> p a
10
ipdb> p b
0
デバッグ中にコードを編集・実行
ipdb
のプロンプトでは、!
を使うことで Python のコードを直接実行できます。これにより、変数の値を変更したり、別の関数を呼び出したりして、挙動を確認できます。
ipdb> p my_variable
'initial_value'
ipdb> !my_variable = 'new_value' # 変数の値を変更
ipdb> p my_variable
'new_value'
ipdb> !import math # モジュールをインポート
ipdb> p math.sqrt(9) # 関数を実行
3.0
また、interact
コマンドを使うと、現在のスコープを引き継いだ IPython シェルが起動します。複数行のコードを試したり、より複雑な操作を行いたい場合に便利です。
ipdb> interact
Entering IPython embed mode. Hit Ctrl-D to exit.
Python 3.9.7 (...)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.30.1 -- An enhanced Interactive Python. Type '?' for help.
In [1]: print(f"Current total: {total}, Current count: {count}")
Current total: 150, Current count: 5
In [2]: for i in range(3):
...: print(i * total)
...:
0
150
300
In [3]: exit() # Ctrl-D でも終了可能
Leaving IPython embed mode.
Returning to ipdb.
ipdb>
設定ファイルによるカスタマイズ
ipdb
の挙動は設定ファイルでカスタマイズできます。~/.pdbrc
または setup.cfg
, pyproject.toml
内の [tool.ipdb]
セクションに設定を記述します。
例えば、.pdbrc
ファイルに以下のように記述すると、起動時に特定のコマンドを自動実行したり、エイリアスを設定したりできます。
# ~/.pdbrc の例
# デフォルトで表示するコンテキスト(ソースコード)の行数を設定
context 10
# コマンドのエイリアスを設定
alias lll longlist
alias ppp pp
# 常に表示したい変数を設定(デバッガ起動時に自動でppコマンドが実行される)
# display some_variable
# display another_variable
pyproject.toml
を使う場合は以下のようになります。
# pyproject.toml の例
[tool.ipdb]
context = 10
# 他の設定...
どのような設定が可能かは、ipdb
や元になっている pdb
のドキュメントを参照してください。
まとめ
ipdb
は、Python標準の pdb
を強化し、IPython
の便利な機能を取り込んだ強力な対話型デバッガです。タブ補完やシンタックスハイライトにより、デバッグ作業の効率と快適さを大幅に向上させます。
基本的なステップ実行や変数確認はもちろん、条件付きブレークポイント、事後デバッグ、対話型シェルでのコード実行など、多様な機能を活用することで、複雑なバグの原因究明もスムーズに行えるようになります。
最初は少し戸惑うかもしれませんが、コマンドに慣れてしまえば、print
デバッグには戻れなくなるほど便利です。ぜひ、日々のPython開発に ipdb
を取り入れて、デバッグ作業を効率化してみてください! 🎉
コメント