具体的なエラーの対処方法(try-except文、else, finally)の使い方について学習したい。
Pythonに限らずプログラミングをしていると、エラーの発生は避けられないものです。
その様なエラーが発生したときに、使用するツールが try-except
文になります。
try-except
文の例外処理とは、予めどういった処理をさせたいかを決め、処理させる事ができます。
これから、エラー
、例外処理
について、try-except
文の使い方、について事例を踏まえて学習していきましょう。
本記事の学習内容
- Pythonにおけるエラーとは
- try-except文の使い方について
- 例外処理が必要な事例について
では早速、学習していきましょう。
Pythonにおけるエラーと例外処理について
プログラムのエラーとは「プログラムが正常に動作できず、処理が中断、停止すること」となります。
Pythonにおいては、エラーは以下の2つのカテゴリーに分ける事ができます。
- 構文エラー( Syntax Error )
- 例外(Exception)
» エラーと例外 - 例外を処理する — Python 3.7.1rc1 【公式ドキュメント】
構文エラーとは
Pythonのコーディング時の文や式が間違っているときに発生するエラーです。
構文エラーは英語では Syntax Error と言われることもあります。
構文エラーには以下の3点があります。
構文エラーの種類
- for文、if文のコロンがない場合
- かっこ書きが不足している場合
- インデントが不足している場合
各々について見ていきましょう。
for文、if文のコロンがない場合
for文、if文にコロンがない場合には、エラーが発生します。
In[]
1 2 | for i in range(3) print(i) |
Out[]
1 2 3 4 | File "<ipython-input-4-72b0e18681b1>", line 1 for i in range(3) ^ SyntaxError: invalid syntax |
エラー修正後はこちらになります。
In[]
1 2 | for i in range(3): print(i) |
次はif文の場合です。
In[]
1 2 3 | import sys if sys.version_info[0]==3 print(sys.version_info) |
Out[]
1 2 3 4 | File "<ipython-input-15-d221c1634ebc>", line 2 if sys.version_info[0]==3 ^ SyntaxError: invalid syntax |
エラー修正後はこちらになります。
In[]
1 2 3 | import sys if sys.version_info[0]==3: print(sys.version_info) |
かっこ書き [ ]
が不足している場合
かっこ書きがいくつもあると、「かっこ」の数が足りなかったり、多かったりでよく出るものです。
Python2系では括弧を付けずにprint関数を呼び出す事ができましたが、Python3系では出来なくなりました。
In[]
1 | list_a = a, b] |
Out[]
1 2 3 4 | File "<ipython-input-20-f77f2f9a456b>", line 1 list_a = a, b] ^ SyntaxError: invalid syntax |
エラー修正後はこちらになります。
In[]
1 | list_a = [a, b] |
インデントがずれている場合
Pythonはif文、for文などインデント(プログラム文前の余白)が必要ですが、それが適切にない場合も構文エラーです。
IndentationError
となっていますが、構文エラーです。
In[]
1 2 | for i in range(3): print(i) |
Out[]
1 2 3 4 | File "<ipython-input-13-78291925d94f>", line 2 print(i) ^ IndentationError: expected an indented block |
エラー修正後はこちらになります。
In[]
1 2 | for i in range(3): print(i) |
例外とは
一方、例外はどういうことかというと、「構文やコードの式は合っているけれども、エラーとなる」ものを指します。
例外は英語ではExceptionとも言われます。
例外の種類=例外クラス はわりと多くあるため、例えば以下のものがあります。
例外の種類
- ZeroDivisionError:ゼロで割り算をした場合に起きるエラー
- TypeError:文字列と数値で計算してしまった場合に発生するエラー
- KeyError:辞書型を使っているときに、存在しない
key
を指定すると発生するエラー - AttributeError:Class(オブジェクト)を使うときに、存在しない属性を指定すると発生するエラー
各々について解説していきます。
ZeroDivisionError
ゼロで割り算した場合です。数値を入れ忘れると発生します。
In[]
1 2 3 | a = 10 b = 0 a/b |
Out[]
1 2 3 4 5 6 7 8 | --------------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) <ipython-input-22-834ceff68cae> in <module> 1 a = 10 2 b = 0 ----> 3 1/b ZeroDivisionError: division by zero |
この計算自体に意味はないですが、エラー修正後はこちらになります。
In[]
1 2 3 | a = 10 b = 1 #ゼロ以外の値 a/b |
TypeError
これは文字列と数値で計算してしまった場合に発生するエラーです。
下記例だと、リストから製品、個数、価格を取り出して、単価を出そうとしたときに、データの取得を間違えて、’appple’ / 10
としているので、エラーが出ています。
In[]
1 2 3 4 5 | # 製品、個数、値段 list_a = ['apple', 10 ,100] num, price, product = list_a # 単価 unit_price = price / num |
Out[]
1 2 3 4 5 6 7 8 | --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-31-600ccdc319d0> in <module> 1 list_a = ['apple', 10 ,100] 2 num, price, product = list_a ----> 3 unit_price = price / num TypeError: unsupported operand type(s) for /: 'int' and 'str' |
エラー修正後はこちらになります。
In[]
1 2 3 4 5 | # 製品、個数、値段 list_a = ['apple', 10 ,100] product, num, price = list_a # 単価 unit_price = price / num |
KeyError
辞書型を使っているときに、存在しない key
を指定すると発生するエラーです。
In[]
1 2 3 4 | dic_animal = {'1': ['レオ', 'ブルドッグ'], '2': ['ポチ', '柴犬'],} dic_animal['1'] dic_animal['3'] |
Out[]
1 2 3 4 5 6 7 8 | --------------------------------------------------------------------------- KeyError Traceback (most recent call last) <ipython-input-30-5f92e0e4fb98> in <module> 2 '2': ['ポチ', '柴犬'],} 3 dic_animal['1'] ----> 4 dic_animal['3'] KeyError: '3' |
修正後はこちらになります。
In[]
1 2 3 4 | dic_animal = {'1': ['レオ', 'ブルドッグ'], '2': ['ポチ', '柴犬'],} dic_animal['1'] dic_animal['2'] |
AttributeError
Class(オブジェクト)を使うときに、存在しない属性を指定すると発生します。
例えば、数値の入った list_a
を考えたときに、sum
という関数がありそうだなと list_a.sum()
としたとします。
実際には、そんな関数はないので、エラーが発生します。実際に例を示してみましょう。
In[]
1 2 | list_a = [1,2,3,4,5] list_a.sum() |
Out[]
1 2 3 4 5 6 7 | --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-35-2ffc8424f572> in <module> 1 list_a = [1,2,3,4,5] ----> 2 list_a.sum() AttributeError: 'list' object has no attribute 'sum' |
修正後はこちら、標準関数の sum
を使用します。
In[]
1 2 | list_a = [1,2,3,4,5] sum(list_a) |
try-except文の使い方
エラーの種類がいくつかあることがわかったので、実際にtry-except文を使って例外処理を行ってみましょう。
エラーがないのが理想ではありますが、try文を使って、エラー処理をするということは、ユーザーに優しいシステムになります。
使っているアプリが強制終了ばかりするものは嫌ですよね。
例外の基本について
一番シンプルな書き方は下記になります。
In[]
1 2 3 4 | try: エラー発生に対応したい処理 except エラーの種類: エラーが発生した時の処理 |
実際に、先ほど0で割ったエラーを例に使ってみましょう。
sys.exc_info()[0]
を実行すると、エラーの種類を返してくれるので、どんなエラーが出たか確認できます。
In[]
1 2 3 4 5 6 7 | import sys try: a = 10 b = 0 a/b except ZeroDivisionError: print("エラーが出ました。:", sys.exc_info()[0]) |
Out[]
1 | エラーが出ました。: <class 'ZeroDivisionError'> |
また、例外クラスの後に as eをつけて、eをprintで表示することで、エラーメッセージを取得することもできます。
In[]
1 2 3 4 5 6 | try: a = 10 b = 0 a/b except ZeroDivisionError as e: print("エラーが出ました。:", e) |
Out[]
1 | エラーが出ました。: division by zero |
例外が複数ある場合について
発生するエラーは色々あり、すべて同じ処理するとは限りません。
その場合は、except エラー内容を複数並べて、それぞれのエラーに合った処理をします。
例えば、先ほどの、単価の計算を例に下記で確認してみましょう。
製品情報(製品, 個数, 合計価格)ごとの単価を出す場合に、個数(num)が文字列だったり、0だったりする場合を考えます。
- numが0なら、ZeroDivisionError が発生するので unit_price = 0
- numが文字列なら、TypeError が発生するので unit_price = -1 とかありえない数字
In[]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | list_a = [['apple', '3', 100], ['orange', 0, 0],] for l in list_a: product, num, price = l try: unit_price = price / num except ZeroDivisionError: print('個数ゼロ') unit_price = 0 except TypeError: print('計算できません') unit_price = -1 l.append(unit_price) list_a |
Out[]
1 2 3 | 計算できません 個数ゼロ [['apple', '3', 100, -1], ['orange', 0, 0, 0]] |
全てのエラーを捕まえたい場合
except
の後のエラーの種類は省略して、ワイルドカード ( wildcard
、総称記号)とすることも可能です。
これはどんなエラーが発生しても処理されることになります。ただあまり推奨されていません。
下記が、非推奨とされているコードです。
In[]
1 2 3 4 5 6 | try: a = 10 b = 0 a/b except: print("エラーが出ました。:", sys.exc_info()[0]) |
Out[]
1 | エラーが出ました。: <class 'ZeroDivisionError'> |
今まで出てきた、「構文エラー」や、「ゼロで割ってはダメ」というエラーを全て捕まえるには、Exceptionを使います。
In[]
1 2 3 4 5 6 | try: a = 10 b = 0 a/b except Exception: print("予期せぬエラーが出ました。:", sys.exc_info()[0]) |
passを使ってエラーを無視
pass とは
実行しても何も起きない。構文上処理記述を求められるが、何の処理もしたくないときに使用します。
try文以外でも使います。
pass はエラーがあっても処理に影響しない場合にエラーを無視して処理をし続けることも可能です。
その場合は、pass
を使います。
そのままの意味ですね、処理しないからパスしてくれということです。
下記を実行してみてください。明らかに、エラーが発生するのですが、pass
としているのでエラーが発生してもパスしてくれています。
In[]
1 2 3 4 5 6 | try: a = 10 b = 0 a/b except Exception: pass |
else、finally
最後に else
と finally
について説明します。
else とは
エラーが発生しないときにする処理を記述するブロックのこと。
finally とは
エラーの発生の有無に関わらず、必ず実行する処理を記述するブロックのこと。
記述ルールは下記になります。
else
, finally
の記述ルール
1 2 3 4 5 6 7 8 | try: エラー発生に対応したい処理 except エラーの種類: エラーが発生した時の処理 else: エラーが発生しない時の処理 finally: エラーが発生してもしなくても最後にする処理 |
処理をイメージにすると下記になります。
finally
はなくても、下記のように、try
文の後に処理を追加すれば同じになりますが、try
で囲った処理に関係する処理だと分かりやすくすることができます。
1 2 3 4 5 6 7 8 | try: エラー発生に対応したい処理 except エラーの種類: エラーが発生した時の処理 else: エラーが発生しない時の処理 次の処理(エラーが発生してもしなくても最後にする処理) |
以下で確認しましょう。
また、製品情報(製品, 個数, 合計価格)で考えます。
追加で、else
の正常に処理ができたときは、sum_price
で合計を計算し、finally
でエラー、正常処理構わず、unit_price
を追加します。
In[]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | list_a = [['apple', '3', 100], ['orange', 0, 0], ['grape', 5, 500],] sum_price = 0 for l in list_a: product, num, price = l try: a = 10 b = 0 unit_price = price / num except ZeroDivisionError: print('個数ゼロ') unit_price = 0 except Exception: print('Error', sys.exc_info()[0]) unit_price = -1 else: print('正常に処理 priceをsum_priceにたす。') sum_price+=price finally: print('単価を追加:', unit_price) print('--------') l.append(unit_price) print('単価つき買い物リスト:', list_a) print('合計価格:', sum_price) |
Out[]
1 2 3 4 5 6 7 8 9 10 11 | Error <class 'TypeError'> 単価を追加: -1 -------- 個数ゼロ 単価を追加: 0 -------- 正常に処理 priceをsum_priceにたす。 単価を追加: 100.0 -------- 単価つき買い物リスト: [['apple', '3', 100, -1], ['orange', 0, 0, 0], ['grape', 5, 500, 100.0]] 合計価格: 500 |
raiseでエラーを発生させる
次に raise
という構文の説明です。
raise とは
エラーを故意に発生させる
記述ルールは以下になります。
1 | raise 例外クラス(メッセージ): |
簡単な例で動かしてみましょう。
In[]
1 2 3 4 | try: raise Exception('error message') except Exception as e: print(e) |
Out[]
1 | error message |
エラーを発生させることができましたね。
なぜ故意に発生させるのでしょうか?
これは、実装する人によって色々あると思いますが以下の例をあげて説明します。
list_a
という商品リスト(商品名、個数、価格)があったとき、個数は必ず int
型であって欲しいとします。
そのとき、list_a
をチェックして、個数が int
でないときにはエラーを発生させてみます。
In[]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import sys list_a = [['apple', '3', 100], ['orange', 0, 0], ['grape', 5, 500],] def list_check(): for l in list_a: if type(l) != int: raise TypeError('個数 type error') try: list_check() except Exception as e: print('Error', e) |
Out[]
1 | Error 個数 type error |
このように、システムを動かすのに不都合がある場合は、エラーを発生させどこが悪いのか気付けるようにします。
例外処理が必要な事例
いくつか、簡単な事例を踏まえて、エラー処理を説明してきましたが、もう少し実際にどうやってエラー処理するかみていきます。
例外処理が必要な具体的事例
ユーザーの入力補助
ユーザーに入力が間違っているときには、適切な値が入力されるように促してみましょう。
下記コードを動かしてみてください。数字が入力されるまで、繰り返されます。
In[]
1 2 3 4 5 6 7 8 | while True: try: num = int(input("何名様ですか?: ")) except Exception: print("すみません、数字を入力ください。") else: print("いらっしゃいませ!", num, "名様ですね。") break |
Out[]
1 2 3 4 5 6 | 何名様ですか?: d すみません、数字を入力ください。 何名様ですか?: e すみません、数字を入力ください。 何名様ですか?: 4 いらっしゃいませ! 4 名様ですね。 |
ファイル操作
例えば、下記中身の、’hoge.txt
’ というファイルを読み込んでいるときにエラーが出たとします。
1 2 3 4 5 | 1 2 three 4 5 |
ファイルは開いたら、開いたままだとエラーの原因になるので、必ず最後に閉じる処理が必要になります。
ですので、下記のように、finally
を使って、必ずファイルを閉じるようにします。
In[]
1 2 3 4 5 6 7 8 9 10 11 | try: print('ファイルオープン') f = open('hoge.txt') for line in f: print(int(line)) except Exception: print('読み取りエラー') finally: # 必ず開いたファイルを閉じる print('ファイルクローズ') f.close() |
Out[]
1 2 3 4 5 | ファイルオープン 1 2 読み取りエラー ファイルクローズ |
エラー時にすること
ログを出す
エラーの原因を調査して、エラーが出ないようにするために、ログで吐き出して、調査ができるようにします。
システム開発はエラーとの戦いです。
エラーの少ないシステムを作るためにも、原因調査ができるログが必要になります。
下記に簡単なコード例です。エラー発生時にerr.logファイルとして出力します。
実行後はファイルの中身を確認してみてください。
In[]
1 2 3 4 5 6 | import datetime as dt try: raise Exception('error message') except Exception as e: with open('err.log', 'a') as f: f.write(dt.datetime.now().strftime('%Y%m%d %H:%M:%S ')+str(e) + '\n') |
Out[]
1 2 | 20200327 14:22:19 error message 20200327 14:22:20 error message |
ユーザーにメッセージを出す
事例でも紹介しましたが、ユーザーは人なので、ヒューマンエラーを前提にシステムを開発します。
なので、ユーザーを補助する上でもエラー時には、ユーザーがどう直せば良いのかメッセージを出します。
また、エラー発生時に強制終了、プシュンとなるアプリは使いたくないものです。
そのために、エラー時にはこんなエラーが出ましたとメッセージを表示してあげて、処理を続けるかどうかを判断したりもします。
コードは事例の「ユーザー入力補助」を参考ください。
終了、継続処理
エラーによっては、継続困難なもの、あまり気にしなくて良いエラーなど様々です。
できれば、エラー発生のたびに強制終了になるのではなく、致命でないエラーはそのまま継続して使えるようにしましょう。
また、明らかにシステムを正常に使うことができないエラーは、メッセージを出して終了してやり直せるようにしましょう。
次の例では、エラー発生時にユーザーに処理継続するか質問しています。
In[]
1 2 3 4 5 6 7 8 9 | while True: try: raise Exception('error message') except Exception: choice = input("Error 続けますか? 'yes' or 'no' [y/N]: ").lower() if choice in ['y', 'ye', 'yes']: print('処理継続') elif choice in ['n', 'no']: break |
Out[]
1 2 | Error 続けますか? 'yes' or 'no' [y/N]: y 処理継続 |
まとめ|Pythonの例外処理【try,except文の扱い方について】
今回はPythonの例外処理について学習をしました。
具体的には構文エラーと例外処理、try-except文の使い方の基本から応用、例外今回はい処理が必要な具体的な事例について解説しました。
例外処理はPythonに限らずプログラミングにおいては必須の知識ですので、この記事でしっかりとまとめておきましょう。
また、今回のテーマであるPythonの例外処理に関する練習問題も以下に用意していますので、是非tryしてみて下さい。
Pythonの例外処理の練習問題【try-except文の使用】
続きを見る
今回は以上となります。