CHAPTER 3 ・ エラーに強いコード / Lesson 11
原因を探す(デバッグの手順)
結果がおかしいとき、闇雲に直さない。途中の値を見て、範囲を狭めて、原因を特定する手順です。
このレッスンが終わったら
「おかしいときは、途中の値を print で確かめ、範囲を半分ずつ狭める。直す前に、原因を言葉にする」と言える。
こんな場面、ありませんか?
営業部の売上を合計するコードを書きました。実行すると、エラーも出ず、数字が表示されます。ところが、電卓で足した 1,110,000円 と、合いません。
▶ 実行を押すと、ここに結果が出ます
730000 と表示されました。3行目も「営業」に見えるのに、足されていません。エラーが出ないバグは、赤い表示というヒントがないぶん、探しにくいものです。
こんなとき、勘で書き換えるのは、遠回りです。この回では、原因を手順どおりに探して、直す方法を学びます。コードの間違いや不具合を直す作業を、デバッグと呼びます。
手順にそって、原因を探す
まず、どこまでが合っていて、どこからおかしいかを調べます。行ごとに、部署の値と、「営業」と等しいかを表示します。
▶ 実行を押すと、ここに結果が出ます
3行目だけが False です。足し算ではなく、「営業と等しいか」の比べ方の問題だと分かりました。次に、その部署の値を、見えない部分まで確かめます。repr()(値を、プログラムに書く形で表示する)と len()(文字数)を使います。
▶ 実行を押すと、ここに結果が出ます
'営業 ' と表示され、文字数は3です。 は全角の空白を表す記号で、見た目には現れず、「営業」の後ろに紛れ込んでいたのです。仮説は「全角の空白があるせいで、一致しない」。strip() で空白を除けば、合うはずです。直して、確かめます。
▶ 実行を押すと、ここに結果が出ます
1110000 になりました。strip() は、全角の空白も取り除きます。
仕組みを読む
いまの流れを、5つの手順にまとめます。おかしいとき、この順に進めます。
- ① 再現する同じ結果を出せる最小のデータで
- ② 範囲を狭めるどこまでが正しいか
- ③ 値を見るprint / repr / type
- ④ 仮説を立てる原因を言葉にする
- ⑤ 直して確認期待した値になったか
エラーが出るときは、トレースバック(エラーまでの道のり)が手がかりになります。基礎編では「最後の2行」を読みました。今回は、道のりの全体を読みます。わざとエラーを出してみましょう。
▶ 実行を押すと、ここに結果が出ます
結果の下の「エラーの全文を見る」を押すと、次のような表示が出ます(行番号以外は、サイトの表示と少し違うことがあります)。
Traceback (most recent call last):
File "<exec>", line 11, in <module>
File "<exec>", line 7, in total_of
File "<exec>", line 2, in read_amount
ValueError: invalid literal for int() with base 10: '3,500'Traceback (most recent call last):ここから、エラーまでの道のりが始まるFile "<exec>", line 11, in <module>11行目で total_of を呼んだ(いちばん外側)File "<exec>", line 7, in total_ofその中の7行目で read_amount を呼んだFile "<exec>", line 2, in read_amountさらにその中の2行目で、エラーが起きた(現場)ValueError: invalid literal ... '3,500'エラーの種類と理由。'3,500' は int にできない読み方は、下から上へです。いちばん下の行で「何が起きたか」を確かめ、その上の行へたどって、自分のコードのどの行かを探します。全文には、サイトの仕組みの行(_pyodide と書かれた行)が混ざることがありますが、<exec> と書かれた行が、あなたのコードです。
変えて、試す
① 範囲を半分ずつ狭める。 処理が何段階もあるとき、真ん中あたりで値を見ます。そこが正しければ、原因は後半に。おかしければ、前半にあります。これを繰り返すと、少ない回数で絞り込めます。金額の下ごしらえで、合計が 5,900 のはずが 4,700 になるコードです。
▶ 実行を押すと、ここに結果が出ます
1: では4件、2: では2件。1と2のあいだで、"800" と "400" が消えています。len(s) > 3 は、空欄を除くつもりが、3文字以下の金額まで除いていました。if s != "" に直せば、5900になります。
② 「コピーしたつもり」のリスト。 リストを別の変数に入れても、コピーにはなりません。
▶ 実行を押すと、ここに結果が出ます
with_tax = prices は、同じリストに、2つ目の名前を付けただけです。元のリストを残したいときは、with_tax = list(prices) のようにコピーを作ります。
自分で書く
どのお題も、そのまま実行すると、正しくない結果になります。原因を言葉にしてから、直してください。
お題① CSVから読んだ金額の中で、いちばん大きい金額を出したい。正しくは 120000 ですが、95000 と出ます。
▶ 実行を押すと、ここに結果が出ます
ヒントを見る
print(type(amounts[0])) で、値の型を確かめてみましょう。文字の大小は、数字の大小とは違う順番で決まります。int() で数字に直してから比べます。
答えの例を見る
amounts = ["95000", "120000", "8000"] print(max(int(a) for a in amounts))
お題② 月ごとの売上の「前の月との差」を出したい。ところが、エラーになります。エラーの最後の2行を読んで、直してください。
▶ 実行を押すと、ここに結果が出ます
ヒントを見る
i が 3 のとき、sales[i + 1] は存在しません。差は、4つの値のあいだに3個だけあります。range の終わりを1つ減らします。
答えの例を見る
sales = [100000, 105000, 98000, 106000] diffs = [] for i in range(len(sales) - 1): diffs.append(sales[i + 1] - sales[i]) print(diffs)
お題③ 税抜の価格を、税込(10%を加え、小数点以下は切り捨て)にする関数です。2つの合計が 3300 になるはずが、エラーになります。
▶ 実行を押すと、ここに結果が出ます
ヒントを見る
エラーの最後の行に出てくる NoneType は、「値がない」という意味です。with_tax(1000) を print して、何が返ってきているか見てみましょう。
答えの例を見る
def with_tax(price): return price * 11 // 10 total = with_tax(1000) + with_tax(2000) print(total)
仕事ならどう使う?
AIに渡す文章
次のPythonコードの結果が、期待と違います。原因を、まず言葉で説明してください。すぐに直したコードを出さず、確認するための print の置き場所も教えてください。【コード】(ここに貼る)【入力の例】(数行)【期待した結果】【実際の結果】【エラーの全文(あれば)】
理解チェックエラーは出ないのに、合計が合いません。最初にすることは、どれでしょう?
理解チェックトレースバック(エラーの全文)の読み方として、正しいのはどれでしょう?