例外処理とは
Pythonでプログラムを書いていると、実行中に予期しないエラーが発生する場合があります。
「ファイルが見つからない」「数字のつもりが文字列だった」など、コードを書く時点では想定しきれない問題が現実には起こりえます。
こうした問題が起きたとき、プログラムをそのまま止めてしまうのではなく、「どう対処するか」をあらかじめ決めておく仕組みが例外処理です。
このセクションでは、その例外処理の基本的な考え方から順を追って解説します。
【関連】Pythonのエラーの種類と対処法を初心者向けに基礎から詳しく解説
例外処理の基本的な意味
例外処理とは、プログラムの実行中に予期しない問題が起きたとき、その問題へ適切に対応するための仕組みです。
日常生活で例えるなら、料理中に材料が切れたとき、そのまま手を止めてしまうのではなく「別の材料で代用する」「買い物に行く」といった対応を取ることに似ています。
プログラムもそうです。問題が起きた場合に「どうするか」をあらかじめ決めておくと、突然止まらずに動き続けられます。
注意ですが、例外処理はエラーを完全になくすわけではありません。
あくまで、起きた問題を検知し、あらかじめ用意した対処を行うための方法です。
「エラーが出ないようにする」のではなく「エラーが出たときにどう動くかを制御する」という考え方が大切です。
出力結果(例)
数字を入力してください:ネコ
数字以外が入力されました上記は、ユーザーが数値を入力する場面を想定した例です。入力値が数字でなかった場合に備えた処理の基本的なイメージになります。
数字以外が入力された場合、その旨の表示がされるようプログラムされているのがわかりますね。
【関連】Pythonをもっと詳しく学ぶならpaizaラーニング
例外とエラーの違い
Pythonでは「エラー」と「例外」は似ているようで、意味が少し異なります。
エラーには大きく分けて2種類あります。
1つ目は、コードの書き方が間違っているために起きる構文エラー(SyntaxError)で、プログラムを実行する前に検出されるエラーです。例えば、コロンの書き忘れや、括弧の閉じ忘れはこのエラーに該当します。
2つ目が、プログラムを実行している最中に発生する問題です。これを例外(Exception)と呼びます。コードの書き方が正しくても、実行時の状況によって発生します。例えば、ゼロで割り算をしようとしたときに起きるZeroDivisionErrorや、存在しないファイルを開こうとしたときに起きるFileNotFoundErrorは、その代表例です。
ただ、日常的な会話やドキュメントでは「エラー」と呼んでも2つ目の意味で通じますし、現場でも「エラーが出た」と言う人がほとんどです。
ValueError TypeErrorのように、名前自体に "Error" が入っていることもあり、間違いとまではいえません。
「エラー」と聞いた際に、「構文エラー」を指すのか、「例外」のエラーなのか判断できれば問題ありません。
ゼロで割り算をしようとしたときに起きるZeroDivisionErrorが発生しているコード例です。
出力結果
Traceback (most recent call last):
File "/workspace/Main.py", line 1, in <module>
result = 10 / 0
~~~^~~
ZeroDivisionError: division by zero2つの違いを表にまとめました。
ここで、しっかりとその違いを押さえておきましょう。
種類 |
発生するタイミング |
主な例 |
|---|---|---|
構文エラー |
実行前(コード読み込み時) |
SyntaxError |
例外 |
実行中 |
ZeroDivisionError、FileNotFoundError |
例外処理が必要になる場面
例外処理が特に必要になるのは、プログラムの外部とやりとりをする処理です。
例えば、ユーザーが数値を入力するフォームでは、誤って文字列が入力される場合もあるでしょう。
ファイルの読み込みでは、そのファイルがそもそも存在しないかもしれません。外部のAPIからデータを取得しようとしても、ネットワークが不調であればデータの取得に失敗します。
このように、ユーザーからの入力、ファイルの読み込み、インターネット経由でのデータ取得などは、どれも「失敗する可能性がある」という共通点があります。
実務では、
「プログラムを止めない」
「何が原因だったかをユーザーや開発者に伝える」
という2つの目的で例外処理が使われます。
エラーの内容を画面に表示して、次にどうすればよいかを案内するだけでも、ユーザーは戸惑わずに次の行動に移れます。
実際、文字をシステムに入力した際にそういう表示を見た経験はありませんか?
出力結果
ファイルが見つかりませんでした。パスを確認してください。例外処理を使わない場合の問題
では、例外処理を書いていないプログラムで予期しない問題が起きた場合、どうなるのでしょうか?
結論からいうと、そのままプログラムは停止します。
もし、ユーザーが入力フォームに予想外の値を入れた直後に、プログラムが突然終了してしまうと、何が起きたのかを知る手がかりが残りません。
開発者にとっては、エラーの原因を調べるのが難しくなります。ユーザーにとっては、何の説明もなしにプログラムが終わってしまうため、次にどう行動すればよいかわかりません。
下の例は、例外処理を書いていない場合にゼロ除算が起きたときの動作です。プログラムはエラーメッセージとともに停止し、その後の処理は一切実行されません。
出力結果
Traceback (most recent call last):
File "/workspace/Main.py", line 1, in <module>
number = int("ネコ")
^^^^^^^^^^^
ValueError: invalid literal for int() with base 10: 'ネコ'このように、例外が発生した後の処理はすべて無視されます。ユーザーへの案内も、ログの記録も、後続の処理も行われないまま終了するということです。
こういった状況を避けるためにも、例外処理を考えておくことが重要です。
例外処理の書き方
Pythonでは例外処理を書くために、try・except・else・finallyという4つのキーワードを組み合わせて使います。それぞれの役割がわかると、状況に合わせた処理を書けるようになるため、この点は押さえておきましょう。
このセクションでは、基本構文から例外が複数ある場合の対応方法まで、順を追って解説します。
try・exceptの基本構文
例外処理の基本構造は、tryブロックとexceptブロックの組み合わせです。
tryの中には、例外が起きる可能性のある処理を書きます。
exceptの中には、例外が発生したときに実行する処理を書きます。
tryの中で問題なく処理が完了した場合、exceptの中身は実行されません。
流れとしては「まず試してみる(try)、もし問題が起きたら対処する(except)」というイメージです。
出力結果
計算中に問題が起きましたtryの中でゼロ除算(0で割る計算)が起きると、処理がexceptブロックに移ります。プログラムは停止せず、exceptの中のメッセージが出力されます。
例外の種類を指定する書き方
exceptの後ろに例外の名前を書くと、特定の種類の例外だけを受け取れます。
例えば、except ValueError:のように書くと、ValueErrorが起きた場合にのみ、そのexceptブロックが実行される形です。他の種類の例外が起きた場合は、そのexceptブロックをスキップして処理されます。
このように例外の種類を指定すれば、どんな問題が起きているのかを正確に把握できますし、各ケースに適した処理を実行できます。
出力結果
数値への変換に失敗しましたまた、as eと書くと、例外の詳細なメッセージを変数に受け取り、表示できます。
出力結果
エラーの内容:invalid literal for int() with base 10: 'トリ'as e は、発生した例外を変数として受け取る書き方です。except ValueError as e のように書くと、例外のメッセージが変数eに入り、print(e) で内容を確認できます。eは変数名なのでerrやerrorでも動きますが、慣習的にeを使うのが一般的です。
ちなみに、このコード例のエラーはint() に変換できない文字列を渡したときに出るエラーです。例えば int("abc") や int("3.14") を実行すると、「これは10進数の整数として読めない」と判断されてしまいます。
メッセージを分解すると、次のような意味です。この機会に、覚えておきましょう。
部分 |
意味 |
|---|---|
invalid literal |
無効なリテラル(値として解釈できない文字列) |
for int() |
int() に渡した |
with base 10 |
10進数として解釈しようとした |
複数の例外を処理する書き方
1つのtryブロックで複数の例外に対応したい場合、exceptを並べて書くと便利です。それぞれの例外へ異なる対処をしたい場合は、この方法を使います。
また、同じ対処を適用したい例外がある場合は、まとめて書くこともできます。
出力結果
リストの範囲外にアクセスしましたまとめて書く場合は、次のようにタプル形式で指定しましょう。
ちなみに、タプル形式とは複数の値をカンマで区切り、丸括弧 () でまとめて書く記法です。except の後ろに (ValueError, TypeError) のように書くと、どちらのエラーが起きても同じブロックで値を受け取れます。
【関連】Pythonのタプルを理解しよう!基礎から実践まで初心者向けに解説
出力結果
値または型に問題がありますelseとfinallyの使い分け
elseとfinallyは、try・exceptに追加できるブロックです。どちらも単独では使えず、tryとセットで機能します。4つのブロックがどのタイミングで動作するかを確認しておきましょう。
ブロック |
実行されるタイミング |
|---|---|
try |
常に(例外が起きるまで) |
except |
例外が発生したとき |
else |
例外が発生しなかったとき |
finally |
例外の有無にかかわらず常に |
elseは、tryブロックの中で例外が発生しなかった場合にだけ実行されます。「問題なく完了した場合にだけ実行する処理」を書く場所です。
finallyは、例外が起きたかどうかに関係なく、必ず最後に実行されます。ファイルを閉じるといった、後始末が必要な処理に向いています。
出力結果
変換成功:42
処理を終了しますぜひ、intの中身を変えて挙動の違いを確認してみてください。
よく使う例外の一覧と具体例
Pythonにはあらかじめ多くの種類の例外が用意されており、それぞれ発生する場面は違います。
どの例外がどんな場合に起きるかを知っておくと、エラーの原因をいち早く特定できるようになりますし、コードを書く際にも便利です。
ここでは、具体的なコードとセットで初心者がよく出くわす代表的な例外について紹介します。
ValueErrorの例
ValueErrorは、関数に渡した値の型は合っているが、中身が期待に合わない場合に発生します。
代表的な例は、int()に数字でない文字列を渡すケースです。int()は整数への変換を試みますが、変換できない内容の場合はValueErrorが発生します。
文字列から数値への変換は、ユーザーの入力を受け取る処理でよく使われるため、ValueErrorへの対処は、ほぼ必須です。
出力結果
ValueErrorが発生しました:invalid literal for int() with base 10: 'ネコ'先述したコード例でも、一度目にしたエラーですね。
もし意味がわからない場合は、振り返っておきましょう。
TypeErrorの例
TypeErrorは、処理に使う値の型が合わない場合に発生します。
例えば、文字列と整数をそのまま足し算しようとすると、Pythonはどう計算すればよいか判断できません。そういった場合に発生するのが、TypeErrorです。
型の問題は、関数への引数の渡し方が間違っている場合や、データの加工途中で思わぬ型の値が混入した場合に起こりやすいので注意しましょう。
出力結果
TypeErrorが発生しました:can only concatenate str (not "int") to strこのエラーは、文字列に整数をそのままつなげようとしたときのエラーです。Pythonでは + で文字列同士を結合できますが、片方がint だと型が違うため実行できません。
str() で変換するか、f文字列を使うのが定番の対処法です。
ちなみに、メッセージを分解すると次のような意味になります。ここで把握しておきましょう。
部分 |
意味 |
|---|---|
can only concatenate str |
文字列につなげられるのは文字列だけ |
(not "int") to str |
intは文字列につなげられない |
IndexErrorとKeyErrorの例
IndexErrorは、リストの存在しない番号(インデックス)を指定したときに発生するエラーです。
また、KeyErrorは、辞書に存在しないキーを指定した際に発生するエラーです。
例外 |
発生する場面 |
|---|---|
IndexError |
リストの範囲外のインデックスにアクセスしたとき |
KeyError |
辞書に存在しないキーを指定したとき |
リストや辞書を扱う処理では、要素が存在するかどうかを確認しないままアクセスすると、この2つの例外はよく起こります。
コード例を確認しておきましょう。
出力結果
IndexErrorが発生しました:list index out of range辞書の場合は、次の通りです。
出力結果
KeyErrorが発生しました:'トリ'FileNotFoundErrorの例
FileNotFoundErrorは、存在しないファイルを開こうとした際に発生します。
ファイルのパスを間違えた場合や、そもそもファイルが作成されていない場合に目にするでしょう。
実行環境によってファイルの保存場所が違うというケースもあるため、ファイル操作を行う処理では必ず対処しておくべきです。
出力結果
FileNotFoundErrorが発生しました:[Errno 2] No such file or directory: 'animals.txt'これは、「'animals.txt' を開こうとしたが、そのファイルが見つからなかった」という意味のエラーです。
メッセージは、次のような意味に分解できます。
部分 |
意味 |
|---|---|
Errno 2 |
OSが返すエラー番号(「ファイルなし」を表す番号) |
No such file or directory |
そのファイルもフォルダも存在しない |
'animals.txt' |
見つからなかったファイル名 |
raiseを使った例外の発生
ここまで、Pythonが自動的に発生させる例外への対処を見てきました。しかし、場合によってはプログラム側から意図的に例外を発生させたい場合があります。そんな場合に使うのがraiseです。
このセクションでは、raiseの意味や書き方、活用場面について解説します。
raiseとは
raiseは、プログラムの中から例外を意図的に発生させるための書き方です。
Pythonが自動で例外を発生させるのと同じように、自分で書いたコードの中からも例外を発生させられます。raiseを使うのは「この値では処理を続けられない」と、コード上で明示したい場合です。
例えば、関数に渡してはいけない値を受け取ったとき、そのまま処理を続けるのではなく、明示的に問題を表示させることができます。
出力結果
TypeErrorが発生しました:動物の名前は文字列で指定してくださいraiseの基本的な書き方
raiseの基本的な書き方は、raise 例外クラス名("メッセージ")です。
例外クラスにはValueErrorやTypeErrorなど、Pythonにあらかじめ用意されているものを使います。
メッセージには、何が問題だったかを説明する文章を書くのが一般的です。このメッセージは、exceptブロックでas eを使うと受け取れます。
出力結果
ValueErrorが発生しました:年齢に負の値は設定できません関数内でのraiseの使い方
raiseは、特定の条件に合わない値が渡されたときに使うケースが多いです。
例えば、年齢や金額、個数など、取りうる範囲が決まっているデータを受け取る関数では、条件を外れた値が来たときにraiseで例外を発生させておくと、問題の検出が早くなります。
問題が発生した場所で止めておくのは、想像以上に重要です。なぜなら、おかしな値がそのまま別の処理に流れ込んで、全く別の場所で突然エラーになると、コードのどの部分に問題があるかわからないからです。問題が起きた場所ですぐ止める方が、格段に原因を特定しやすくなります。
次のコードは、0以下の値が来た場合に例外を発生させる例です。
出力結果
ValueErrorが発生しました:パンダの体重は0より大きい値を指定してくださいraiseとreturnの違い
returnとraiseはどちらも関数の実行を終わらせるという特徴がありますが、役割が全く異なります。
returnは、正常に処理が完了した時点で結果を呼び出し元へ返すための書き方です。一方、raiseは「この処理は正常に続けられない」という情報を知らせるために使います。
処理の結果として値を渡したい場合はreturn、処理を続けられない状況を知らせたい場合はraiseを使いましょう。
書き方 |
用途 |
呼び出し元での扱い |
|---|---|---|
return |
正常な結果を返す |
戻り値として受け取る |
raise |
異常な状況を知らせる |
exceptで受け取る |
例外処理のベストプラクティス
例外処理は、ただ書けばいいというものではありません。どんな例外を受け取るべきか、エラーが起きたときに処理を続けるべきかどうかなど、判断が必要な場面は意外と多いのです。
書き方によっても、コードの読みやすさや安全性が大きく変わるため、ベストプラクティスの考え方を知っておくのは重要になります。
ここでは、初心者が迷いやすいポイントと、実務でも使える書き方のコツを紹介します。
広すぎるexceptを避ける考え方
実は、except:と書くだけで、すべての例外をまとめて受け取ることができます。しかし、この書き方は、本来気付くべきバグまで隠してしまうため、原則として使うべきではありません。
例えば、コードの中に変数名の間違いがあった場合でも、except:だけで書いていると、その問題もすべて飲み込んでしまいます。プログラムは止まらないまま、誤った結果を出し続ける状態になりかねません。
例外を受け取るときは、どんな問題を想定しているかを意識して、具体的な例外名を指定するようにしましょう。
出力結果
何かが起きました
ゼロで割ることはできません例外処理で終了させる場合の注意点
例えば、数字を入力すべき場面で文字を入力してしまった場合は、ユーザーが入力し直せば解決できます。
一方、在庫データの取得に失敗した場合は、ユーザー側では対処できないため、そのまま処理を進めるのは危険です。在庫データがないのに、購入の手続きが完了してしまい、実際には商品が届かない……なんてことになりかねません。
今回はイメージしやすい例を挙げましたが、システムによってはより致命的なインシデントにつながる場合もあります。「やり直しが効く場合はもう一度試すよう促し、そうでない場合は早めに止めてメッセージを出す」という点を基本的な判断の軸として覚えておきましょう。
出力結果
必要なファイルが見つかりません。処理を中止します。エラーメッセージの書き方
エラーメッセージは、「何が起きたか」と「次に何を確認すればよいか」の2点を伝えるようにしましょう。
「エラーが発生しました」という文言だけでは、ユーザーも開発者も原因の特定ができません。
どのデータで、どんな問題が起きたのかを具体的に書くと、対処がしやすくなります。
出力結果
ファイル 'animals.csv' が見つかりませんでした。パスとファイル名を確認してください。with文や条件分岐との使い分け
例外処理は、あらゆる問題に使うべきではありません。「この値は範囲内か」や「リストに要素はあるか」など、事前に確認できる内容はif文で書くほうがシンプルです。
if文は、データの中身やプログラムの状態を事前にチェックして、問題が起きるのを未然に防ぐために使いましょう。
例外処理は、事前にチェックするのが難しい、または予防不可能なハプニングが起きてしまった場合に、プログラムを安全に不時着させるために使うイメージです。ちなみに、ファイル操作を行う処理ではwith文を使うのがおすすめです。
try・finallyではなくwithを使うと、処理が終わった後にファイルを自動で閉じることができます。コードの記述が短いですし、閉じ忘れによるトラブルも防げるというのもメリットです。
下記は、if文を使用したコード例です。
出力結果
指定した番号は範囲外ですよくある質問(Q&A)
Q. tryの中にtryを書いてもいいですか?
A. はい、ネストするのは可能です。ただし、ネストが深くなるとコードが読みにくくなるため、関数に分割するなどして整理するのをおすすめします。
Q. 例外が起きなくてもfinallyは実行されますか?
A. はい。finallyは例外の有無にかかわらず、必ず実行されます。ファイルを閉じるといった後始末の処理に使います。
Q. 自分で例外の種類を作ることはできますか?
A. できます。Exceptionを継承したクラスを定義すると、独自の例外を作れます。ただし初心者のうちは、Pythonに用意されている例外の使用で十分なケースがほとんどです。
Q. exceptとelseは同時に使えますか?
A. はい。try・except・else・finallyはすべて同時に使えます。elseは例外が起きなかったときだけ実行される処理を書く場所です。
Q. エラーの内容を表示したいときはどうしますか?
A. except ValueError as e:のようにasを使うと、例外の詳細を変数eに受け取れます。print(e)で内容を表示できます。
まとめ
例外処理は、プログラムの実行中に起きる想定外の問題へ対応するための仕組みでしたね。
エラーを完全になくすことが目的ではありません。問題が起きた際にプログラムが適切に動き続けられるよう制御するのが目的です。
例外処理が有用なのは、次のような場面です。
例外処理が活躍する場面
- ユーザーからの入力を受け取るとき
- ファイルの読み込みや書き込みをするとき
- 外部のAPIやネットワーク通信を使うとき
例外処理を書く上で、押さえておきたいポイントを振り返りましょう。
重要なポイント
- 例外処理は、エラーが起きた際の対処をあらかじめ決めておく仕組み
- try・exceptが基本の書き方で、else・finallyで処理の流れを細かく制御できる
- 例外の種類はValueError・TypeErrorなどのように、具体的に指定する
- raiseを使うと、プログラム側から意図的に例外を発生させることができる
- エラーメッセージでは「何が起きたか」と「次にすべきアクションは何か」を具体的に伝える
ユーザー入力・ファイル操作・外部データの取得など、失敗する可能性がある処理に対して例外処理を書けるのは、実用的なプログラムを作る上で欠かせないスキルです。
例外処理をマスターすると、エラーが起きても大きな問題なく対処できるプログラムが書けるようになります。
それは同時に、ユーザーにとって使いやすいツールやサービスを届けられるということです。
まずは、この記事で紹介した基本的な書き方を参考にして、実際にコードを書いて試してみてください。