追記 2018/05/23: Python の例外に興味がある方には次の記事も参考になるかもしれません。よろしければご覧ください。
Python における例外処理について説明してみたいと思います。
Python では、例外処理「 try ... catch ...
」のロジックは try ```` except
というキーワードを使って実現することができます。
以下、最も基本的な形から順に Python での例外処理の方法について見ていきましょう(ここでご紹介する記法は Python 2.x のものです。 Python 3.x の場合は少し異なるのでご注意ください)。
try
- except
最も基本的な形は try
と except
を使ったパターンです。
try:
f = open('sample.txt', 'r')
except:
print 'cannot open sample.txt'
この場合、 try
のブロックの中にある処理の中で例外(エラー)が発生したら except
ブロックへと処理が移ります。
try
ブロックの中で例外が発生しなかった場合は、 except
のブロックは実行されず、その後の処理へと進んでいきます。
try
- except ExceptionName
キーワード except
に後にスペースを挟んで例外のクラスを指定すると、キャッチする例外を絞り込むことができます。
try:
f = open('sample.txt', 'r')
except IOError:
print 'cannot open sample.txt'
このコードの場合だと、入出力に関わる IOError
という例外のみがキャッチされます。その他の例外があがった場合には except
ブロックの処理は実行されません。
exept
ブロックは複数個セットすることができます。例えば、次のようにすると、 PermissionError
が
あがったときとその他の例外があがったときとで表示するメッセージを切り替えることができます。
import sys
try:
f = open('sample.text', 'r')
except PermissionError:
print 'パーミッションエラーです。'
except Exception:
print 'その他の例外です。'
except
ブロックは上から順番に一致チェックがかかり、マッチした最初の except
ブロックのコードのみが実行されることに注意してください。
つまり、上のコードの場合だと、 try
ブロックで PermissionError
があがった場合には「パーミッションエラーです。」というメッセージのみが表示され、「その他の例外です。」というメッセージは表示されません。処理の流れのイメージとしては、 if ... elif ... elif
に近いイメージです。
上から順にチェックがかかるので、プログラマーは「捕捉範囲の狭い例外を先に、範囲の広い例外を後に記述すること」を心がける必要があります。具体的には、 Python では例外はクラスとして表現されているので、これは「子クラスを先に、親クラスを後に記述すること」を意味します。
ちなみに Python 2.x の場合、組み込みの例外には次のような親子関係があるので、複数の except
ブロックを書く場合は、これを確認した上で記述するようにするとよいでしょう。
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- GeneratorExit
+-- Exception
+-- StopIteration
+-- StandardError
| +-- BufferError
| +-- ArithmeticError
| | +-- FloatingPointError
| | +-- OverflowError
| | +-- ZeroDivisionError
| +-- AssertionError
| +-- AttributeError
| +-- EnvironmentError
| | +-- IOError
| | +-- OSError
| | +-- WindowsError (Windows)
| | +-- VMSError (VMS)
| +-- EOFError
| +-- ImportError
| +-- LookupError
| | +-- IndexError
| | +-- KeyError
| +-- MemoryError
| +-- NameError
| | +-- UnboundLocalError
| +-- ReferenceError
| +-- RuntimeError
| | +-- NotImplementedError
| +-- SyntaxError
| | +-- IndentationError
| | +-- TabError
| +-- SystemError
| +-- TypeError
| +-- ValueError
| +-- UnicodeError
| +-- UnicodeDecodeError
| +-- UnicodeEncodeError
| +-- UnicodeTranslateError
+-- Warning
+-- DeprecationWarning
+-- PendingDeprecationWarning
+-- RuntimeWarning
+-- SyntaxWarning
+-- UserWarning
+-- FutureWarning
+-- ImportWarning
+-- UnicodeWarning
+-- BytesWarning
Python 2.x 、 3.x それぞれの組み込みの例外について詳しくは公式ドキュメントを参照ください。
try
- except ExceptionName, var
キャッチした例外の中身を確認したい場合は、 except
の行を exept 例外クラス, 変数名
と書く形になります。
try:
f = open('sample.txt', 'r')
except Exception, e:
print e, 'error occurred'
必ずそうしないといけない決まりはありませんが、変数名には exception
または省略形の e
が選ばれていることが多いようです。
追記 20171222: Python 3.x の場合はここの記法が少し異なり、次のように記述する形になっています。
try:
f = open('sample.txt', 'r')
except Exception as e:
print(e, 'error occurred')
try
- except
- else
except
ブロックの後には、オプションで else
ブロックをつけることができます。
try:
f = open('sample.txt', 'r')
except:
print 'cannot open sample.txt'
else:
lines = f.readlines()
else
ブロックに記述された処理は、 try
ブロックの中で例外が発生しなかった場合にのみ実行されます。
try
- except
- finally
else
ブロックと同じく、オプションで finally
ブロックをつけることができます。
finally
ブロックの中の処理は、 try
ブロック内での例外の発生の有無にかかわらず実行されます。
try:
f = open('sample.txt', 'r')
except:
print 'cannot open sample.txt'
finally:
f.close()
else
と finally
の両方を記述する場合は、 else
を先に、 finally
を後に記述するルールになっています。
try:
# 例外が発生するかもしれない処理
except:
# 例外が発生したときの処理
else:
# 例外が発生しなかったときの処理
finally:
# 例外の発生の有無にかかわらず必ず実行されるべき処理
finally
ブロックは基本的には try
から始まる一連の処理の最後に実行されますが、 try
のブロックの中で例外が発生したのにそれががどの except
でもキャッチされなかった場合には finally
ブロックの処理が実行された後に try
の中であがった例外が再度投げ直されるようになっています。
また、 try
ブロックの中で break ```` continue ```` return
のいずれかが実行されたときにはそこから脱出する間際に finally
ブロックが実行されるようになっているようです。
このあたりの流れは少し複雑なので、 finally
を正しく使いたい場合には 公式のドキュメント などをきちんと確認したうえで使うようにされるのがよいかと思います。
以上です。
例外処理に関しては、次のように PEP20 Zen of Python でも言及されています。
Errors should never pass silently. Unless explicitly silenced.
このあたり、 Python の言語設計のベースの考え方とも直結しているみたいです。
参考