CHAPTER 3 ・ エラーに強いコード / Lesson 10

入力をチェックして、自分でエラーを出す

金額がマイナス、日付が未来、必須項目が空。ルール違反を見つけて、分かりやすい理由つきで知らせます。

目安 20分 ・ インストール不要

このレッスンが終わったら

「raise で自分でエラーを出せる。チェックのルールは関数にまとめ、理由が分かるメッセージを付ける」と言える。

STEP 1

こんな場面、ありませんか?

社員から集めた経費の申請データ expenses.csv を、経理でまとめて精算します。まず、件数と合計を出してみましょう。

expenses.csvサンプルファイル
申請ID,申請日,申請者,部署,区分,金額,摘要,領収書
E001,2025-10-01,田中,営業,交通費,1240,客先訪問 東京→横浜,あり
E002,2025-10-01,佐藤,営業,会議費,3600,打合せ コーヒー,あり
E003,2025-10-02,鈴木,企画,消耗品費,8800,コピー用紙,あり
E004,2025-10-02,高橋,企画,接待交際費,42000,取引先 会食,あり
E005,2025-10-03,田中,営業,交通費,1240,客先訪問 東京→横浜,あり
E006,2025-10-04,伊藤,総務,消耗品費,15400,文房具まとめ買い,なし
E007,2025-10-05,佐藤,営業,交通費,980,客先訪問,あり
E008,2025-10-06,鈴木,企画,会議費,2400,,あり
E009,2025-10-07,高橋,企画,接待交際費,98000,取引先 会食 5名,あり
E010,2025-10-07,田中,営業,交通費,15600,大阪出張 新幹線,あり
E011,2025-10-08,伊藤,総務,通信費,5500,携帯電話,あり
E012,2025-10-08,佐藤,営業,会議費,4800,打合せ 軽食,あり
E013,2025-10-09,鈴木,企画,交通費,2100,セミナー参加,あり
E014,2025-10-09,高橋,企画,消耗品費,1980,USBケーブル,あり
E015,2025-10-10,田中,営業,接待交際費,36000,取引先 会食,あり
E016,2025-10-11,伊藤,総務,消耗品費,3300,ゴミ袋,あり
E017,2025-10-12,佐藤,営業,交通費,1240,客先訪問 東京→横浜,あり
E018,2025-10-14,鈴木,企画,会議費,52000,社内懇親会,あり
E019,2025-10-14,高橋,企画,交通費,760,客先訪問,なし
E020,2025-10-15,田中,営業,交通費,1240,客先訪問 東京→横浜,あり
E021,2025-10-15,田中,営業,交通費,1240,客先訪問 東京→横浜,あり
E022,2025-10-16,伊藤,総務,消耗品費,6600,トナー,あり
E023,2025-10-17,佐藤,営業,接待交際費,24000,取引先 会食,あり
E024,2025-10-18,鈴木,企画,交通費,1800,打合せ,あり
E025,2025-10-20,高橋,企画,会議費,3000,打合せ お茶,あり
E026,2025-10-21,伊藤,総務,通信費,5500,携帯電話,あり
E027,2025-10-22,田中,営業,交通費,-1240,取消 客先訪問,あり
E028,2025-10-23,佐藤,営業,会議費,5200,打合せ 軽食,あり
E029,2025-10-24,鈴木,企画,消耗品費,9900,書籍,あり
E030,2025-10-27,高橋,企画,交通費,980,客先訪問,あり
件数と合計を出すサンプルファイル付き・書き換えOK
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

エラーなく、合計が出ました。でも、この合計は、そのまま精算に使ってよいのでしょうか。実はこのデータには、マイナスの金額、摘要の空欄、高額なのに領収書がない申請が混ざっています。Pythonは、こうした「おかしなデータ」を、黙って計算に入れてしまいます。

人が目で探すと、見落とします。この回では、おかしなデータを Python に点検させて、理由つきで報告させる方法を学びます。この「入力のチェック」を、バリデーションと呼びます。

STEP 2

自分でエラーを出してみる

Lesson 09 では、Python が出すエラーを受け止めました。今度は反対に、自分でエラーを出します。使うのは raise(レイズ:「発生させる」)です。

金額がマイナスなら、エラーを出すコードは書き換えられます
Ctrl / ⌘ + Enter でも実行
結果
▶ 実行を押すと、ここに結果が出ます

1240円は通り、-1240円のところで、自分で書いたメッセージつきのエラーになりました。except で受け止めなければ、Python 自身のエラーと同じように、プログラムはそこで止まります(Step 4 で確かめます)。

STEP 3

仕組みを読む

raise ValueError("理由") の部品
raiseここでエラーを出す
ValueErrorエラーの種類(値がおかしいとき)
("金額がマイナスです")人に伝える理由(かっこの中)
チェックする関数を、1行ずつ日本語にする
def check_amount(amount):金額をチェックする関数を作る
if amount < 0:もし金額がマイナスなら
raise ValueError(f"金額がマイナスです: {amount}円")理由つきで、ValueError を出す(ここで関数は終わる)
return amount問題がなければ、金額をそのまま返す

エラーの種類は、「値の中身がおかしい」ときは ValueError を使うのが基本です。迷ったら ValueError で構いません。

理由の書き方で、受け取る人の動きやすさが変わります。何行目の・何が・どうなっていて・どうすればよいかを入れましょう。

分かりにくい

raise ValueError("不正")

分かりやすい

raise ValueError(f"{line_no}行目({id}): 金額が-1240円です。取消か入力ミスか、確認してください")
「不正」だけでは、どこを直せばよいか分かりません。場所・状態・次にすることを入れます。
STEP 4

変えて、試す

① ルールを関数にして、全件に当てる。 1つのルールを1つの関数にして、リストに並べます。全件・全ルールを当てて、エラーは止めずに集めて、最後に一覧で報告します。「領収書なし」の基準は社内ルールで決まるので、ここでは仮に1万円以上とします。

ルール関数を全件に当てて、一覧にするサンプルファイル付き・書き換えOK
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

3件の指摘が、理由つきで並びました。新しいルールを足すときは、関数を1つ書いて RULES に加えるだけです。指摘は「間違い」の断定ではなく、人が確認する候補です。たとえば -1240円は、正しい取消の入力かもしれません。

② 最初のエラーで止まると? try を外して、同じ点検をしてみます。

わざと:エラーを集めずに、そのまま raise させるサンプルファイル付き・書き換えOK
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

9行目の空欄で止まり、28行目のマイナスの金額は、点検されないままです。全件を点検して一覧にしたいときは、try / except で受け止めて集めます。 反対に、入力フォームのように「1件でもおかしければ先へ進めない」処理では、止まるのが正しい動きです。

③ assert(アサート)。 assert は、「ここでは必ず、この条件が成り立つはず」という自分への確認です。成り立たないと AssertionError で止まります。

assert で、自分の計算を確かめるコードは書き換えられます
Ctrl / ⌘ + Enter でも実行
結果
▶ 実行を押すと、ここに結果が出ます

使い分けは、こうです。外から来るデータ(CSV・入力)のチェックには raise。自分のコードの途中で、「こうなっているはず」を確かめるのが assert です(Lesson 28 の「テスト」でも使います)。

STEP 5

自分で書く

お題① 金額が0以下のとき、ValueError("金額は1円以上にしてください") を出す関数 check_amount を書いてください。

お題①:ルール関数を書くコードは書き換えられます
Ctrl / ⌘ + Enter でも実行
結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

def check_amount(value): の中で、if value <= 0: と書き、その中に raise ValueError("金額は1円以上にしてください") と書きます。

答えの例を見る
def check_amount(value):
  if value <= 0:
      raise ValueError("金額は1円以上にしてください")

for value in [1200, 0, -300]:
  try:
      check_amount(value)
      print(value, "OK")
  except ValueError as e:
      print(value, e)

お題② 領収書のルール check_receipt を書いてください。「金額が10000円以上で、領収書が『なし』」なら ValueError を出します。指摘された申請IDの一覧を表示します。

お題②:違反の一覧を作るサンプルファイル付き・書き換えOK
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

def check_receipt(row): の中で、if row["領収書"] == "なし" and int(row["金額"]) >= 10000: と書き、その中で raise ValueError(...) とします。

答えの例を見る
import csv

with open("expenses.csv", encoding="utf-8") as f:
  rows = list(csv.DictReader(f))

def check_receipt(row):
  if row["領収書"] == "なし" and int(row["金額"]) >= 10000:
      raise ValueError("領収書がありません")

bad_ids = []
for row in rows:
  try:
      check_receipt(row)
  except ValueError:
      bad_ids.append(row["申請ID"])
print(bad_ids)

お題③ 日付の文字を受け取る関数 check_date を書いてください。date.fromisoformat で日付に直し(Lesson 03)、2025年10月1日〜31日の外なら ValueError("対象期間の外です")、日付の形でなければ ValueError("日付の形式が違います") を出します。

お題③:日付が範囲内かをチェックするコードは書き換えられます
Ctrl / ⌘ + Enter でも実行
結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

try: d = date.fromisoformat(text) / except ValueError: raise ValueError("日付の形式が違います") と書きます。そのあとで、if not (START <= d <= END): と調べます。

答えの例を見る
from datetime import date

START = date(2025, 10, 1)
END = date(2025, 10, 31)

def check_date(text):
  try:
      d = date.fromisoformat(text)
  except ValueError:
      raise ValueError("日付の形式が違います")
  if not (START <= d <= END):
      raise ValueError("対象期間の外です")

for text in ["2025-10-15", "2025-11-02", "2025/10/15"]:
  try:
      check_date(text)
      print(text, "OK")
  except ValueError as e:
      print(text, e)
STEP 6

仕事ならどう使う?

理解チェックraise ValueError("理由") を実行すると、どうなりますか?

理解チェック全件を点検するとき、エラーを止めずに集めてから、まとめて報告する理由はどれでしょう?