7. エラーと向き合う

前回は関数について学びました。今回は、プログラミングで避けられない「エラー」との付き合い方を学んでいきましょう。

7.1 エラーとは?よくあるミスとその原因

エラーは「プログラムが上手く動かないとき」に発生するメッセージです:

よくあるエラーの例

# よくあるエラーの例

# 1. NameError: 存在しない変数を使おうとした
print(undefined_variable)  # 定義していない変数を使用

# 2. TypeError: 異なる型の演算
text = "123"
result = text + 456  # 文字列と数値の足し算

# 3. IndexError: リストの範囲外にアクセス
numbers = [1, 2, 3]
print(numbers[5])  # 存在しない位置を指定

# 4. ZeroDivisionError: 0での割り算
result = 10 / 0  # ゼロ除算

# 5. ValueError: 不適切な値の変換
number = int("abc")  # 数値に変換できない文字列

よくあるエラーの原因と対策

  • 変数名のタイプミス → スペルチェック
  • カッコの対応忘れ → エディタの補完機能を使う
  • インデントのズレ → 視覚的に確認する
  • データ型の誤り → 型変換を適切に行う

7.2 try-except でエラーに負けないプログラム作り

try-exceptを使うと、エラーが発生しても処理を続行できます:

基本的なtry-except

# 基本的なtry-except
try:
    # エラーが発生するかもしれない処理
    number = int(input("数字を入力してください:"))
    print(f"入力された数字は{number}です")
except ValueError:
    # エラーが発生したときの処理
    print("数字以外が入力されました")

# 複数のエラーを扱う
def divide_numbers():
    try:
        num1 = int(input("1つ目の数字:"))
        num2 = int(input("2つ目の数字:"))
        result = num1 / num2
        print(f"結果:{result}")
    except ValueError:
        print("数字を入力してください")
    except ZeroDivisionError:
        print("0で割ることはできません")
    except:  # その他のすべてのエラー
        print("予期せぬエラーが発生しました")

7.3 エラーメッセージを読んで問題箇所を特定する

エラーメッセージには重要な情報が含まれています:

エラーメッセージの例

"""
TypeError: can only concatenate str (not "int") to str
    at line 15 in file "example.py"
    text = "答え:" + 42

このエラーメッセージから分かること:
1. エラーの種類:TypeError(型の不一致)
2. 発生場所:15行目
3. 原因:文字列と整数の連結
"""

# 修正例
text = "答え:" + str(42)  # 整数を文字列に変換してから連結

# エラーメッセージの主な要素
"""
1. エラーの種類(TypeError, ValueError など)
2. エラーの説明文
3. エラーが発生したファイル名と行番号
4. 問題のあるコードの部分
"""

7.4 print デバッグでコードを細かくチェック

print文を使って、プログラムの動きを確認できます:

printデバッグの例

def calculate_total(items):
    total = 0
    print(f"計算開始:items = {items}")  # デバッグ用出力

    for item in items:
        print(f"処理中:item = {item}")  # 各ステップの確認
        total += item
        print(f"現在の合計:{total}")    # 中間結果の確認

    print(f"計算終了:total = {total}")  # 最終結果の確認
    return total

# プログラムの実行
numbers = [10, 20, 30]
result = calculate_total(numbers)

"""
出力例:
計算開始:items = [10, 20, 30]
処理中:item = 10
現在の合計:10
処理中:item = 20
現在の合計:30
処理中:item = 30
現在の合計:60
計算終了:total = 60
"""

7.5 コメントや整形でバグを減らす工夫

コードを整理することで、バグを防ぐことができます:

コメントや整形でバグを減らす工夫

# 悪い例
def calc(x,y,z):
    a=x+y
    if a>10:b=a*z
    else:b=a/z
    return b

# 良い例
def calculate_result(base_num, add_num, multiplier):
    """
    2つの数の和を計算し、条件に応じて乗算または除算を行う

    Args:
        base_num: 基準となる数
        add_num: 加算する数
        multiplier: 乗除算に使う数

    Returns:
        計算結果
    """
    # 最初の2つの数を加算
    sum_result = base_num + add_num

    # 合計が10より大きければ乗算、それ以外は除算
    if sum_result > 10:
        final_result = sum_result * multiplier
    else:
        final_result = sum_result / multiplier

    return final_result

7.6 あえてエラーを起こして練習する

エラーを理解するために、意図的にエラーを発生させてみましょう:

あえてエラーを起こして練習する

def practice_errors():
    # 1. NameError
    try:
        print(undefined_name)
    except NameError as e:
        print(f"変数が定義されていません:{e}")

    # 2. TypeError
    try:
        result = "hello" + 123
    except TypeError as e:
        print(f"型が合っていません:{e}")

    # 3. IndexError
    try:
        list_data = [1, 2, 3]
        print(list_data[10])
    except IndexError as e:
        print(f"リストの範囲外です:{e}")

# 各種エラーを体験
practice_errors()

7.7 テスト用のチェックを追加する

テストコードを書いて、プログラムの動作を確認できます:

テスト用のチェックを追加する

def test_calculation_functions():
    """計算関数のテスト"""

    # 足し算のテスト
    assert add(2, 3) == 5, "2 + 3 は 5 になるはず"
    assert add(-1, 1) == 0, "-1 + 1 は 0 になるはず"

    # 掛け算のテスト
    assert multiply(2, 3) == 6, "2 * 3 は 6 になるはず"
    assert multiply(0, 5) == 0, "0 * 5 は 0 になるはず"

    print("すべてのテストが成功しました!")

def add(a, b):
    return a + b

def multiply(a, b):
    return a * b

# テストの実行
try:
    test_calculation_functions()
except AssertionError as e:
    print(f"テストが失敗しました:{e}")

課題 1. 入力ミスがあっても止まらず再入力を促す計算プログラム

入力ミスがあっても止まらず再入力を促す計算プログラム

def get_number(prompt):
    """数値を安全に入力させる関数"""
    while True:
        try:
            value = int(input(prompt))
            return value
        except ValueError:
            print("数字を入力してください")

def safe_calculator():
    """エラーに強い電卓プログラム"""
    while True:
        try:
            # 数値の入力
            num1 = get_number("1つ目の数字を入力:")
            num2 = get_number("2つ目の数字を入力:")

            # 演算子の入力
            operator = input("演算子を入力(+, -, *, /):")

            # 計算実行
            if operator == "+":
                result = num1 + num2
            elif operator == "-":
                result = num1 - num2
            elif operator == "*":
                result = num1 * num2
            elif operator == "/":
                if num2 == 0:
                    print("0での割り算はできません")
                    continue
                result = num1 / num2
            else:
                print("無効な演算子です")
                continue

            # 結果表示
            print(f"結果:{result}")

            # 継続確認
            if input("続けますか?(y/n):") != "y":
                break

        except Exception as e:
            print(f"エラーが発生しました:{e}")
            print("もう一度試してください")

# プログラムの実行
safe_calculator()

課題 2. 存在しないファイルを開こうとしたときにエラーを回避するプログラム

存在しないファイルを開こうとしたときにエラーを回避するプログラム

def safe_file_reader(filename):
    """ファイルを安全に読み込む関数"""
    try:
        with open(filename, "r", encoding="utf-8") as file:
            content = file.read()
            return content
    except FileNotFoundError:
        print(f"ファイル '{filename}' が見つかりません")
        return None
    except PermissionError:
        print(f"ファイル '{filename}' にアクセスする権限がありません")
        return None
    except Exception as e:
        print(f"予期せぬエラーが発生しました:{e}")
        return None

def process_file():
    """ファイル処理プログラム"""
    while True:
        filename = input("処理するファイル名を入力してください:")

        # ファイルの読み込み
        content = safe_file_reader(filename)

        if content is not None:
            # ファイルの内容を処理
            print("\nファイルの内容:")
            print(content)

            # 文字数をカウント
            char_count = len(content)
            line_count = len(content.splitlines())

            print(f"\n文字数:{char_count}")
            print(f"行数:{line_count}")

        # 継続確認
        if input("\n他のファイルを処理しますか?(y/n):") != "y":
            break

# プログラムの実行
process_file()

# チャレンジ:
# - ファイルの内容を別のファイルにコピーする機能を追加
# - ファイルの内容を行ごとに分析する機能を追加
# - 複数のファイルを一度に処理する機能を追加

まとめ

この章で学んだこと:

  • エラーは学習の機会として捉える
  • try-exceptでエラーに対応できる
  • デバッグ方法の使い分け
  • コードの整理でバグを防ぐ
  • テストでプログラムの品質を保つ

次の章では、データの処理と可視化について学び、より実践的なプログラミングスキルを身につけます!