print() 要らず?たった1行でコードを止められる breakpoint()
バグを見つけるとき、とりあえず print(変数名) を挟んで実行し、コンソールに流れる文字を目で追いかけていませんか?
実はPythonには、コードの好きな場所で処理を一時停止して、その場で変数の中身を自由に確認できる仕組みが標準で用意されています。それが breakpoint() です。
この記事では、print() デバッグしか知らない方向けに、breakpoint() と pdb の最低限の使い方を、実務で困らない範囲でまとめます。
難しいオプションは省き、まずは「これだけ知っていれば」というラインに絞っています。
そもそもbreakpoint()とは何か
breakpoint() は、呼び出された場所でプログラムの実行を一時停止し、対話的なデバッガ(デフォルトでは標準ライブラリの pdb)を起動する組み込み関数です。
Python 3.7で追加され、それ以前に使われていた import pdb; pdb.set_trace() を1行で置き換えられるようになりました。
つまり、止めたい場所に次の1行を書くだけです。
def calculate_total(prices, discount_rate):
total = sum(prices)
breakpoint()
return total * (1 - discount_rate)
calculate_total([1000, 2000, 3000], 0.1)
このコードを実行すると、breakpoint() の位置でプログラムが止まり、ターミナルに (Pdb) というプロンプトが表示されます。この状態になれば、その時点でのローカル変数を自由に確認できます。
(Pdb) p total
6000
(Pdb) p discount_rate
0.1
(Pdb) n
(Pdb) continue
print() と違って、後から「あ、これも見たかった」と思った変数を、コードを直して再実行することなくその場で確認できるのが最大のメリットです。
覚えるコマンドは5つで十分
pdb には多くのコマンドがありますが、実務で困らないためにまず覚えるべきものは以下の5つだけです。
| コマンド | 意味 |
|---|---|
p 変数名 |
変数の中身を表示する(printの略) |
n |
次の1行を実行する(関数の中には入らない) |
s |
次の1行を実行する(関数の中にも入る) |
c |
次のbreakpointか終了まで実行を継続する |
q |
デバッグを中断してプログラムを終了する |
慣れてきたら l(現在地周辺のコードを表示)や w(呼び出し元をさかのぼって表示)も便利ですが、最初はこの5つの往復だけでほとんどの調査が完結します。
本番コードに残すと事故る、忘れずに対処すること
breakpoint() を書いたままコードをコミットしてしまい、本番環境やCIでプログラムが無言のまま固まる、というのはよくある事故です。ここは必ず押さえておいてください。
breakpoint() が本番コードに残っていると、対話入力を待つプロンプトが標準入力を持たない環境(CIサーバーやDockerコンテナなど)で固まり続ける原因になります。
デバッグが終わったら削除するか、次に紹介する環境変数で無効化してください。
Pythonは PYTHONBREAKPOINT という環境変数を見ており、これを 0 に設定すると、コード中の breakpoint() をすべて何もしない扱いに変えられます。
コードを1行も書き換えずに一括で無効化できるので、削除し忘れが心配な場合はこちらが安全です。
# breakpoint()を全部無視して実行する
PYTHONBREAKPOINT=0 python your_script.py
逆に言うと、削除するのを忘れても、本番実行時にこの環境変数さえ設定しておけば止まらずに済むということです。
とはいえ根本的にはコードから消すのが一番なので、あくまで保険と考えてください。
好みのデバッガに差し替えられる
PYTHONBREAKPOINT には 0 以外にも、任意の関数を指定できます。
たとえば、標準の pdb より補完や色付けが効いた ipdb を使いたい場合は、次のようにします。
pip install ipdb
PYTHONBREAKPOINT=ipdb.set_trace python your_script.py
こうしておくと、コード側の breakpoint() はそのままで、実行時の環境変数だけで挙動を切り替えられます。
チームメンバーごとに好きなデバッガを使い分けられるのも breakpoint() の地味に嬉しいポイントです。
print()を使うとき、これを知っておくと得
しかし「ちょっと値を見るだけなら print() で十分」という場面も多いはずです。
その場合は、Python 3.8以降で使える自己文書化式(f"{変数=}")を知っておくと、変数名を書く手間が省けます。
total = 6000
print(f"{total=}")
# total=6000 と表示される
print(f"total: {total}") のように自分で変数名の文字列を書く必要がなく、タイプミスによる勘違いも減らせます。
ちょっとした確認であれば、こちらのほうが手早いこともあります。
まとめ
-
breakpoint()はPython 3.7から使える組み込み関数で、呼んだ場所でプログラムを一時停止できる - 最初に覚えるコマンドは
pnscqの5つで十分 - 本番コードに残すのが不安な場合は、環境変数
PYTHONBREAKPOINT=0で一括無効化できる -
PYTHONBREAKPOINTに他の関数を指定すれば、ipdbなどのデバッガにも差し替えられる - ちょっとした確認だけなら
f"{変数=}"も便利な選択肢
print() を挟んでは消す、を繰り返していた作業が、breakpoint() を1行書くだけの作業に変わります。次にバグに出会ったときは、ぜひ一度試してみてください。