デバッグ
デバッグ
最初は関数なしで
まずは、小さめのプログラムのデバッグをしてみましょう。
以下で用いる例は、debug_test2.pyです。 下記プログラム文面を、コピペして使ってください。
このプログラムは、
- 0.1 * 0.1 + 0.2 * 0.2 + … + 0.9 * 0.9
を計算するつもり(そうはなっていない)のプログラムです。
now = 0.1
result = 0.0
count = 1
# 0.1*0.1 + 0.2*0.2 + ... + 0.9*0.9 をするつもりのプログラム
# now を 0.1, 0.2 と進めていく
while now < 1.0:
result += now * now
now += 0.1
count += 1
print("result:", result)
# 1^2 + 2^2 + ... + n^2 = n*(n+1)*(2n+1)/6 なので、
ans = (9 * 10 * 19)/600.0
print("Ans:", ans)
動作させると、なぜか、
result: 3.8499999999999996
Ans: 2.85
という結果になってしまいます。誤差って訳じゃなくて、正解と 1 の差があります。
ってことで、よく分からないものは、デバッガにかけて調べましょう。 (一瞬で答えが分かったという人も、今後に備えて、デバッガの使い方を学びましょう)
デバッガ起動
デバッグ実行には、画面上の「虫」のアイコンを選択します。
プログラムを特定の行で停止したい場合は、行番号のところをクリックして、ブレークポイントを指定できます。とりあえず、7行目にブレークポイントを入れた状態で、デバッガを開始したところです。
Step 実行関係のパネルと Continue, Stop 関係が分かれて配置されています。
-
step over: 一行実行(関数があっても次の行に)
-
step in: 一行実行(関数があったら、関数の中の行に)
-
step out: 実行中の関数を完了し、呼び出し側に
-
restart: 再起動
-
continue: 続行(ブレークポイントまで進む)
-
stop: 停止
1行実行
プログラムを一行ずつ進めてみましょう。step over
ボタンを押してください。どのボタンが対応するのかは、マウスオーバーすると教えてくれるはず。
- プログラムが一行進み
- 変数
now, result, count
が順に初期化されていく
のが見えるはず。少しループも含めてプログラム実行を追っかけましょう。
breakpoint & Continue
1行ずつ走らせるのが面倒になったので、Continue (続行)で走らせましょう。ブレークポイントで停止するか、終了するまで走ります。 今回の場合、ループが1周進むわけですね。
そうこうしていると、バグの原因っぽいものを目撃することになります。
関数の例
今度は、関数を実行したときの変数の扱いを見ていきましょう。
python では、以下の二つの変数があります。
- グローバル変数:関数外で作られた(代入された)変数、関数内で読出し可能(書込みは少し手間)
- ローカル変数:関数内で作られた(代入された)変数、その関数実行内でのみ読み書き可能
scope_test.py というプログラムを準備しました。
count_a(line)
: 文字列line
中のa
の数を数える関数line, num_a, c
などのローカル変数をもつ
- scope_test.py: N 個の文字列を読込み、
a
の数が一番多い文字列を出力するプログラムN, kouho, max_count, line0, ans0
などのグローバル変数を利用
デバッガで関数のなかを追っかけたかったら、Step in
で関数内に入っていくか、あるいは関数内に breakpoint を設けるといいでしょう。
N = int(input()) # このあたりはグローバル変数
kouho = ""
max_count = -1
g_ans = 0
def count_a(line): #line はローカル変数
num_a = 0 # num_a, c はローカル変数
for c in line:
if c == 'a':
num_a += 1
g_ans = num_a # 実はグローバル変数アクセス「じゃない」
return num_a
for _ in range(N):
line0 = input() # このあたりもグローバル変数
ans0 = count_a(line0) # このあたりもグローバル変数
# print('msg:', line0, ans0, g_ans, max_count)
if ans0 > max_count:
kouho = line0
max_count = ans0
print(kouho)
関数呼び出しは、こんな感じ
- for 文の中で、
count_a(line0)
が呼ばれ、 count_a()
では、line0
の値を、line
として受け取る。
関数実行中は、現在実行中の関数のローカル変数
のみが見えるようになります。
実は、関数呼び出し関係は、コールスタック
タブを開くと確認できて、その際、
- 呼出し元の関数を選ぶと、そのローカル変数を確認でき、
<module>
を選ぶとグローバル変数が確認できます。
ローカル変数が「それぞれの関数実行ごとにある」って感覚をつかんでください。 関数から関数が呼ばれた時は、スタックフレームが呼ばれただけ増えて、関数が終われば戻っていきます。
で、デバッガ使っていると、ローカル変数とグローバル変数、両方みたいなんてこともよくあります。
そういう時は、ウォッチ式
のところに、指定した式の内容を表示できますので、そちらを使いましょう。