Python | Web / API:デバッグ基礎

Python Python
スポンサーリンク

概要(デバッグは「原因を特定して直すための再現と観察」)

デバッグの核心は、問題を確実に再現し、プログラムの状態を正しく観察して、原因に最短で辿り着くことです。初心者は「再現条件を固定する」「トレースバック(エラーメッセージ)を読む」「最小例に切り詰める」「変数の中身と処理の流れを可視化する」を型として身につけると、作業が劇的に楽になります。


再現から始める(条件を固定して、最小例へ切り詰める)

再現条件の固定

エラーは毎回同じ入力・設定で再現できる状態にします。乱数・現在時刻・外部APIレスポンスなど、結果が変わる要素は「固定値」に置き換えます。これにより、修正の効果を正しく検証できます。

最小再現例を作る

問題に関係ないコードは削ぎ落とし、数十行以内で同じ不具合が出る「最小例」を作ります。余計な依存がない方が、原因が浮かび上がります。最小例は、質問やバグ報告にも使える「強い武器」になります。


トレースバック(エラー表示)を読む(どこで何が起きたか)

どの行で、どんな種類のエラーか

Pythonのトレースバックは、最後の行が「エラーの種類」と「一言の説明」、その直前までが「どのファイルの何行目で起きたか」を示します。まず“最も下の原因行”を開いて、そこにある変数の値と前提が期待どおりかを確認します。

def div(a, b):
    return a / b

print(div(1, 0))
Python

実行例(読み方の要点):

  • ZeroDivisionError(0で割っている)
  • example.py の div 関数の行で発生 まず b の値が想定どおりか、ゼロを許してよい仕様かを検討します。

観察の方法(print → logging → デバッガの段階的活用)

まずは print で「どこで何が入っているか」を見る

最短で効果があるのは、疑わしい箇所に print を挿入し、入力値・分岐結果・返り値を出す方法です。多用すると散らかるので、原因が絞れたら削除するか、logging に置き換えます。

def fetch_price(tax_rate, price):
    print("DEBUG:", tax_rate, price)  # 一時的な観察
    return price * (1 + tax_rate)
Python

logging に切り替えて「綺麗に記録する」

print は一時観察、logging は運用・原因追跡向けです。レベルとフォーマットを使い、時刻や行番号付きで記録します。例外は logger.exception でスタックトレースごと残します。

import logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(lineno)d %(message)s")

try:
    1/0
except Exception:
    logging.exception("計算に失敗")
Python

デバッガで止めて、覗き、1行ずつ進める

ブレークポイント(止めたい行の指定)→実行→停止中に変数を確認→ステップ実行(1行ずつ進める)という流れで、「想定と現実の差」を正確に把握します。条件付きブレークポイント(特定の値の時だけ止める)を使うと、再現が難しいバグも楽になります。


標準デバッガ pdb(最小のツールで確実に原因へ)

最短の使い方

疑わしい行にブレークポイントを置き、実行中に止めて観察します。

def total(items):
    import pdb; pdb.set_trace()  # ここで停止
    s = 0
    for x in items:
        s += x
    return s

print(total([1, 2, 3]))
Python

停止中に使う基本コマンド(代表例):

  • n(next): 1行進める
  • s(step): 関数の中へ入る
  • c(continue): 次のブレークポイントまで進める
  • p 変数名: 変数の中身を表示
  • l(list): 近辺のソース表示

条件付きで止める

特定のケースだけ止めたい時は、分岐の中へ置くか、条件を満たしたら set_trace を呼ぶ設計にします。大量ループで全部止まるのを避けられます。


思考の型(仮説→観察→反証→修正のループ)

仮説を立てて、最短観察で反証する

「ここで None が入っている」「この分岐は常に false」など、1つの仮説に絞って観察(print/log/デバッガ)で確認します。外れたら次の仮説へ。複数仮説を同時に追うと、観察が散らかり時間を浪費します。

修正は小さく、直後に再現テスト

1行〜数行の変更に留め、再現条件で直ちに再実行します。修正の副作用を避けるため、広範囲の書き換えはしないのが鉄則です。


例題で身につける(入力・分岐・外部連携の定番バグ)

例題1:型ズレで期待どおりに計算されない

def calc_total(price, tax_rate):
    return price * (1 + tax_rate)

print(calc_total("1000", 0.1))  # 文字列 * float → 型ズレ
Python

観察ポイント:

  • 入力の型を print で出す(type(price))
  • 変換を入れる(int(price))か、早期バリデーションで弾く

修正例:

def calc_total(price, tax_rate):
    if not isinstance(price, (int, float)):
        raise TypeError("priceは数値を指定してください")
    return price * (1 + tax_rate)
Python

例題2:分岐の条件が常に意図と逆に評価される

def is_premium(user):
    return user.get("score", 0) > 100  # 100以上がプレミアムのつもり

u = {"score": 100}
print(is_premium(u))  # False → 条件の境界ミス
Python

観察ポイント:

  • 条件の境界(>= か > か)を明示し、テストに境界値を必ず含める

修正例:

def is_premium(user):
    return user.get("score", 0) >= 100
Python

例題3:外部APIの失敗が静かに飲み込まれる

import requests

def fetch_user(uid):
    r = requests.get(f"https://httpbin.org/status/500")
    if r.status_code == 200:
        return r.json()
    return None  # 失敗理由が分からない
Python

観察ポイント:

  • 例外を出す(raise_for_status)
  • 失敗時にログへ理由を残す

修正例:

import logging, requests
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")

def fetch_user(uid):
    try:
        r = requests.get(f"https://httpbin.org/get", timeout=5)
        r.raise_for_status()
        return r.json()
    except requests.RequestException as e:
        logging.error("API失敗: %s", e)
        return None
Python

##落とし穴の回避(例外処理・ミュータブル・環境差)

例外を広く握りつぶさない

except Exception: pass のような記述は原因を隠します。最低でもエラーメッセージを記録し、必要な範囲の例外型だけを捕捉します。

ミュータブルなデフォルト引数に注意

関数定義で list や dict をデフォルトにすると、呼び出し間で共有されて意図せず蓄積します。None を使って中で生成するのが安全です。

環境差(バージョン・依存)を固定する

仮想環境で同じ依存を使い、requirements.txtで固定します。環境が違うと、同じコードでも再現しません。まず環境を可視化し、ズレをなくすことが近道です。


まとめ(“再現→観察→小さく修正”を型にする)

デバッグは「再現を固め、観察して、最小の修正を試す」作業です。トレースバックで原因行へ直行し、print→logging→デバッガの順で可視化を深める。仮説はひとつずつ検証して反証し、修正直後に再現テストを回す。例外は記録し、ミュータブルな罠を避け、環境差を固定する。この型を体に入れれば、初心者でも短い手順で原因へ辿り着けるようになります。

タイトルとURLをコピーしました