Featured image of post 初心者が取り組むはじめてのPythonコードデバッグ @ VS Code

初心者が取り組むはじめてのPythonコードデバッグ @ VS Code

これはなに Link to this heading

VS CodeにPythonの拡張機能 を入れると、VS CodeでPythonコードをデバッグできるようになる。 ただ、この機能をきちんと使ったことがないため、すこし使ってみた。 その際やったことを記録しておく。

環境 | 前提条件 Link to this heading

対象とするPythonコード Link to this heading

次のPythonコードをデバッグの対象とする。

src/main.py
1
2
3
4
5
6
7
8
def add_to_list(i: int, i_list: list = []) -> list:
    i_list.append(i)
    return i_list


print(add_to_list(1))
print(add_to_list(2))
print(add_to_list(3))

リストに値を追加するだけの関数である。 追加用のリストが指定されない場合は、空のリストに与えられた値を追加したい。 すなわち、理想の出力は下記である。

理想の出力
$ python src/main.py
[1]
[2]
[3]

しかし、これをそのまま実行すると下記の結果が得られる。

実際の出力
$ python src/main.py
[1]
[1, 2]
[1, 2, 3]

おかしいなあ…。なんでぇ…?

デバッグする Link to this heading

何が想定通りに動いていないのかを見る。

ブレークポイントを打つ Link to this heading

最初に、詳細を確認したいところにブレークポイントというものを打つ。 VS Codeでブレークポイントを打つには、打ちたい行の左側をクリックするか、その行にカーソルを合わせてF9を押す。

今回は、add_to_list()の結果をprintするところに打ってみる。

ブレークポイントを打つ前 ブレークポイントを打った後

デバッグモードで実行する Link to this heading

VS CodeにPythonの拡張機能 を入れていれば、エディタの左タブにある「デバッグして実行(Run and Debug)」タブが使える。Ctrl + Shift + Dでも開ける。

VS Codeのデバッグして実行(Run and Debug)タブ

このタブの「デバッグして実行(Run and Debug)」ボタンをクリックすると、デバッグ構成を指定するよういわれる。 今回は「Pythonファイル」を選ぶ。

デバッグ構成の指定

すると、Pythonコードが最初のブレークポイントの手前まで実行される。

最初のデバッグ実行時

コードをデバッグする Link to this heading

デバッグモードで実行した時点では、まだprint(add_to_list(1))が実行されていない。そのため、関数add_to_listの変数などは定義されておらず、何も表示されない。

この状態になったら、F11を押すか、下図のツールボックスの「ステップイン(Step Into)」をクリックする。

デバッグ用ツールボックス。左から3番目の下矢印がステップイン。

「ステップイン」は、その行が関数の場合、関数の中に入り、その関数の1行目の実行前まで進む。 その行が関数でない場合は、1行だけ実行し次の行へ進む。

今回はadd_to_list関数でステップインを実行するため、add_to_listの中に入り、その関数の1行目の実行前まで進むことになる。 すなわち、ii_listが定義された状態に移行する。

ステップインした状態

左側のVARIABLESで、i1i_list[]であると確認できる。 これは想定通りの動作である。

次に、この状態で「ステップオーバー」を実行する。 「ステップオーバー」を実行するには、F10を押すか、下図のツールボックスの「ステップオーバー(Step Over)」をクリックする。

デバッグ用ツールボックス。左から2番目の時計回り矢印がステップオーバー。

「ステップオーバー」を実行すると、その行が関数か否かにかかわらず1行を実行し、次の行の実行前まで進む。 今回の場合はi_list.append(i)だけが実行される。

ステップオーバーした状態

左側のVARIABLESで、i_list[]から[1]に変化した。 ii_listにきちんと追加されている。

最初の呼び出しではadd_to_listがきちんと動作しているとわかったため、次の呼び出しに進む。 次のブレークポイントまで進むには「続行」を実行する。「続行」を実行すると、現在の行から次のブレークポイントまでのコードを実行し、次のブレークポイントの前まで進める。

「続行」するには、F5を押すか、「続行(Continue)」をクリックする。

デバッグ用ツールボックス。一番左の右矢印が続行。

これで、次のブレークポイント、すなわち2回目のadd_to_list呼び出し前まで進む。

続行した後の状態

2回目のadd_to_list呼び出し前まで進んだので、「ステップイン」して呼び出しの中身を見てみる。F11を押すか、ツールボックスの「ステップイン(Step Into)」をクリックする。

2回目の呼び出しにステップインした状態

左側のVARIABLESを見る。すると、2回目のadd_to_list呼び出しでは、初期値のi2だが、i_listは空ではなく[1]になってしまっているとわかる。 これは想定動作と異なる。想定では、i_listを引数で指定していない場合は空のリストになってほしいのに、そうなっていないのだ。

ちなみに、この状態でステップオーバーすると、i_list2が追加され、i_list[1, 2]となる。

ステップオーバーした状態

すなわち、関数add_to_listが想定通りの動作をしなかったのは、少なくとも2回目のadd_to_listの呼び出しの時点で、i_listが空のリストに初期化されていないからだとわかる。

コードを修正する Link to this heading

i_listが空のリストにきちんと初期化されていないのが原因であるから、きちんと初期化されるようにすればよい。

src/main.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
def add_to_list(i: int, i_list: list | None = None) -> list:
    if i_list is None:
        i_list = []
    i_list.append(i)
    return i_list


print(add_to_list_2(1))
print(add_to_list_2(2))
print(add_to_list_2(3))

これで想定通りの結果が得られる。

コード修正後の実際の出力
$ python src/main.py
[1]
[2]
[3]

やったね!

おわりに Link to this heading

あくまで使ってみた機能は少しだけである。WATCHとかCALL STACKとかは使ってないのでよくわからない。 ただ、今回使ってみた機能だけでもわざわざprintしなくてよくなるため、デバッグ効率が上がりそう。

ちなみに Link to this heading

今回のデバッグ対象コードの元ネタはこれ。

この仕様を初めて知ったときは恐怖を覚えた。

Licensed under CC BY-NC-SA 4.0
最終更新 5月 21, 2023
Hugo で構築されています。
テーマ StackJimmy によって設計されています。