Pythonプログラムの開発において、バグの特定と修正は避けて通れない作業です。
多くの開発者が最初に手にする武器は print() 関数による変数表示ですが、複雑なロジックや大規模なシステムでは、出力量が増えすぎて本質的な原因を見失うことが少なくありません。
そこで重要になるのが、Pythonに標準搭載されているデバッガ pdb(Python Debugger) です。
pdbを活用することで、プログラムを任意の場所で一時停止させ、その時点での変数の状態を確認したり、一行ずつコードを実行したりすることが可能になります。
GUIベースのIDE(統合開発環境)に備わっているデバッガも便利ですが、サーバー環境やコマンドライン上でのデバッグにおいて、標準ライブラリのみで完結するpdbの習得は エンジニアとしての対応力を飛躍的に向上させます。
本記事では、pdbの基本的な使い方から、現場で役立つ便利なコマンド、効率的なデバッグの手順まで詳しく解説します。
Python pdbとは何か
pdbは、Pythonの標準ライブラリとして提供されているインタラクティブなソースコードデバッガです。
特別なインストール作業を必要とせず、Pythonが動作する環境であればどこでも利用できるのが最大の強みです。
デバッガの主な役割は、プログラムの実行を制御し、内部の状態を詳細に検査することにあります。
pdbを使用することで、以下のような操作が可能になります。
- プログラムを特定の行で停止させる(ブレークポイント)。
- 停止した時点でのローカル変数やグローバル変数の値を確認する。
- ソースコードを一行ずつ実行し、処理の流れを追跡する。
- 実行中に変数の値を書き換え、挙動の変化を確認する。
- 関数の呼び出しスタックを遡り、どこから呼ばれたかを特定する。
現代の開発ではVS CodeやPyCharmといった高機能なエディタのデバッガが主流ですが、リモートサーバー上のトラブルシューティングや、Dockerコンテナ内でのクイックな確認においては、pdbのようなCLI(コマンドラインインターフェース)ベースのツールが決定的な役割を果たします。
pdbを起動する3つの方法
pdbを起動する方法には、主に3つのパターンがあります。
開発の状況やデバッグしたい対象に合わせて使い分けるのが効率的です。
1. breakpoint() 関数を使用する(推奨)
Python 3.7以降、デバッグを開始するための組み込み関数として breakpoint() が導入されました。
これが現在、最も一般的で推奨される方法です。
def calculate_price(unit_price, quantity):
# ここで実行を停止し、デバッガを起動する
breakpoint()
total = unit_price * quantity
return total
result = calculate_price(100, 5)
print(result)
このコードを実行すると、breakpoint() の行に到達した時点でプログラムの実行が一時停止し、プロンプト (Pdb) が表示されます。
ここから対話形式でデバッグコマンドを入力できます。
2. インポートして set_trace() を呼び出す
Python 3.6以前や、レガシーなコードベースでよく見られる手法です。
pdb モジュールをインポートし、明示的に set_trace() を呼び出します。
import pdb
def divide_numbers(a, b):
# 古い形式のデバッグポイント
pdb.set_trace()
return a / b
divide_numbers(10, 2)
breakpoint() 内部ではこの処理が呼び出されているため、基本的には breakpoint() を使用すれば問題ありません。
3. コマンドラインからモジュールとして実行する
ソースコードを一切書き換えたくない場合や、プログラムの冒頭からデバッグを開始したい場合に有効な方法です。
python -m pdb myscript.py
> /path/to/myscript.py(1)<module>()
-> def my_function():
(Pdb)
この方法で起動すると、プログラムの1行目で停止した状態からスタートします。
ここからブレークポイントを設定したり、ステップ実行を開始したりすることができます。
pdbの基本操作コマンド一覧
デバッガが起動した後は、コマンドを入力してプログラムを制御します。
主要なコマンドを以下の表にまとめました。
多くのコマンドには短縮形が用意されており、効率的に操作できます。
| コマンド | 短縮形 | 機能の概要 |
|---|---|---|
list | l | 現在実行中の行周辺のソースコードを表示する |
next | n | 次の行まで実行する(関数の中には入らない) |
step | s | 次の行まで実行する(関数があれば中に入る) |
continue | c | 次のブレークポイントまでプログラムを実行し続ける |
print | p | 指定した変数の値を表示する |
pp | 変数の値を整形して表示する(Pretty Print) | |
break | b | 新しいブレークポイントを設定する |
where | w | 現在の呼び出しスタックを表示する |
quit | q | デバッガを終了し、プログラムを強制終了する |
help | h | 利用可能なコマンドの一覧や詳細を表示する |
実行を制御するコマンドの使い分け
最も頻繁に使用するのは n (next) と s (step) です。
- next (n) は「現在の行を実行して次へ進む」という意味です。もしその行に関数呼び出しがあっても、その関数の中身は詳細に見ず、実行結果だけを受け取って次の行へ進みます。
- step (s) は「次に行われる処理へ一歩進む」という意味です。呼び出している関数がある場合、その 関数内部の最初の1行目まで移動 します。自作関数の内部でバグが疑われる場合は
sを、組み込み関数や信頼できるライブラリの呼び出しを飛ばしたい場合はnを使うのが定石です。
変数を確認するコマンド
プログラムが停止している際、そのスコープにある変数を調べることができます。
# (Pdb) プロンプトでの操作例
(Pdb) p my_variable
100
(Pdb) pp large_dict
{'id': 1,
'meta': {'created_at': '2026-01-01', 'status': 'active'},
'name': 'Sample Data'}
p は単に値を表示しますが、pp は辞書型やリスト型などの複雑なデータ構造を見やすく整形してくれます。
実践的なデバッグの流れ
ここでは、具体的にバグを含んだコードを例に、pdbを使ってどのように原因を突き止めるかの流れをシミュレーションします。
サンプルコード:平均計算のバグ
以下のコードは、数値のリストを受け取り、その平均値を計算する関数ですが、空のリストが渡された場合にエラーが発生します。
def get_average(numbers):
total = sum(numbers)
count = len(numbers)
# ここにバグの懸念があると仮定
breakpoint()
average = total / count
return average
data = []
print(f"Average: {get_average(data)}")
デバッグのステップ
実行と停止:
スクリプトを実行すると、breakpoint()で停止します。変数の確認:
まず、計算に使われる変数の状態を確認します。(Pdb) p total
0
(Pdb) p count
0ここで、
countが 0 であることがわかりました。次に実行されるのがtotal / countなので、ゼロ除算エラー(ZeroDivisionError)が発生することが予見できます。スタックの確認:
この関数がどこから呼ばれたかを確認するにはwを使います。(Pdb) w
/path/to/script.py(10)<module>()
-> print(f"Average: {get_average(data)}")
> /path/to/script.py(6)get_average()
-> average = total / countこれにより、呼び出し元で渡された
dataが空であることがエラーの根本原因だと特定できます。動的な修正の試行:
デバッガ内で変数の値を書き換え、エラーを回避できるか試すこともできます。(Pdb) count = 1
(Pdb) n
> /path/to/script.py(7)get_average()
-> return average
(Pdb) p average
0.0countを無理やり 1 にすることで、エラーを出さずに進めることが確認できました。これにより「countが 0 の場合のガード節が必要だ」という解決策が明確になります。
効率を高める応用テクニック
pdbをさらに使いこなすための高度なテクニックを紹介します。
ブレークポイントの動的な管理
プログラム実行中に、特定の行に新しくブレークポイントを追加したい場合は b (break) コマンドを使用します。
b 15: 現在のファイルの15行目にブレークポイントを設定。b my\_function:my_function関数の入り口に設定。b: 設定されているブレークポイントの一覧を表示。
また、条件付きブレークポイント も非常に強力です。
(Pdb) b 20, x > 100
これは「20行目で止まる。ただし、変数 x が 100 を超えている場合のみ」という指示になります。
ループ処理の中で、特定の条件下だけでバグが出る場合に極めて有効です。
実行中のスタック移動
u (up) コマンドと d (down) コマンドを使用すると、呼び出しスタック内を移動できます。
u: 呼び出し元の関数(親フレーム)に移動。d: 呼び出し先の関数(子フレーム)に移動。
移動した先のスコープで変数を確認できるため、「呼び出された関数側に問題があるのか、それとも渡された引数自体が既に間違っていたのか」を柔軟に調査できます。
ポストモーテムデバッグ(事後解析)
プログラムが例外を投げてクラッシュしてしまった後、その時点の状態を確認したいことがあります。
これを「ポストモーテムデバッグ」と呼びます。
import pdb
def problematic_code():
x = 10
y = 0
return x / y
try:
problematic_code()
except Exception:
import sys
# 例外が発生した直後の状態でデバッガを起動
pdb.post_mortem(sys.exc_info()[2])
この手法を使えば、「なぜエラーが起きたのか」を、エラー発生時点の変数の中身を見ながら解析 できます。
大規模なデータ処理中に予期せぬ入力で止まった際などに重宝します。
IDEのデバッガとの使い分け
モダンな開発環境では、pdbを直接使う機会は減っているかもしれません。
しかし、以下のようなケースではpdbが圧倒的に優位です。
- GUIがない環境: クラウド上の仮想マシンや、SSHで接続したLinuxサーバー。
- CI/CDパイプライン: 自動テスト中にエラーが発生した際、一時的にデバッグコードを仕込んでログ上で確認する場合。
- 軽量なデバッグ: 重いIDEを立ち上げるまでもなく、スクリプトの断片をさっと確認したい場合。
- 再現困難なバグ: 実行環境に依存するバグの場合、開発機ではなく本番に近い環境のCLIで叩く必要があるため。
逆に、コード全体を俯瞰しながら視覚的にブレークポイントを管理したり、複雑なオブジェクトをツリー形式で展開して見たい場合は、VS CodeなどのIDEデバッガを活用するのが最適です。
両者を状況に応じて使い分けられることが、プロフェッショナルな開発者の条件といえます。
まとめ
Pythonの標準デバッガであるpdbは、シンプルながらも非常に強力なツールです。
基本的な操作である n (next)、s (step)、c (continue)、p (print) を覚えるだけでも、デバッグの効率は劇的に向上します。
特に breakpoint() 関数の導入により、以前よりもデバッグの開始が容易になりました。
「とりあえず print() を書く」という習慣から一歩進んで、pdbでスマートに内部状態を追いかける手法を身につけましょう。
デバッグは単なる「間違い探し」ではなく、プログラムの挙動を深く理解するためのプロセスでもあります。
pdbという強力な相棒を使いこなすことで、複雑なロジックに対しても自信を持ってコードを書き、修正できるようになるはずです。
今回紹介したコマンドやテクニックを、ぜひ日々の開発に取り入れてみてください。
