LoginSignup
5
7

More than 3 years have passed since last update.

Pythonの例外処理についてまとめてみた

Last updated at Posted at 2020-02-04

例外処理

今日はエラーについて解説していきます。初歩的な内容ですが、エラーなんてtry~except書いときゃいいんでしょ?という方は、参考にしてみてください。また、説明に誤りがある場合コメントをいただけると幸いです。

さて、Pythonで生じるエラーは主に二つに大別されます。構文エラー(syntax error)例外(exception)です。

構文エラー

これは、プログラムを実行する前から誤っていると判断できる際に起こります。例えば、識別子(identifier)の規則を満たしていない変数名を使ったり、インデントがおかしかったり、とにかく文法がおかしい時に生じるエラーのことです。a[0]a[0}と書いたり、ifofと書いたり...(これやったことありますか?僕はあります...)例を上げきれませんね。簡単に言えば、構文エラーはプログラムを書く練習をしていれば減ってくる文法ミスです。一応例を書いておきます笑

#構文エラーの例
a = [1, 2, 3]
print(a[3})

出力

    print(a[3})
             ^
SyntaxError: invalid syntax

例外

これはプログラムを実行中にコンピュータが(インタプリタが?)「こいつは処理できないぞ?」と判断した際に生じるエラーです。簡単な例で言えば


a = [1, 2, 3]
print(a[3])

出力

      1 a = [1, 2, 3]
----> 2 print(a[3])

IndexError: list index out of range

配列の要素はインデックス番号2までしか存在しませんから、a[3]を出力しようとすると当然例外が発生します。ここでは例外の例としてIndexErrorを示しましたが、まだまだたくさんあります。

例外を捕捉する

さて、プログラムを実行した時に、例外が発生しても処理を続けたい場合があります。その際はお馴染みのtry~exceptを使います。例外処理によってZeroDivisionErrorが出てもプログラムが止まらないようにしてみましょう。


a = 1
b = 0

try:
    print(a/b)
except ZeroDivisionError:
    print('0で割っちゃダメでしよ?ZeroDivisionErrorがでたでし')
finally:
    print('割り算はむずかちぃでし')

出力

0で割っちゃダメでしよ?ZeroDivisionErrorがでたでし
割り算はむずかちぃでし

上の例のように、最後の後始末的にfinallyをつけてもいいです。これは例外が発生しようがしまいが最後に実行され、そこで処理が中断、終了します。

複数の例外を捕捉する

exceptには、次のようにタプルで複数の例外を捕捉できるよう指定できます。


try:
    a = int(input('a = '))
    b = int(input('b = '))
    print(a/b)
except (ValueError, ZeroDivisionError):
    print('ValueErrorかZeroDivisionErrorが起こっているでし')

出力1

a = 1.5
ValueErrorかZeroDivisionErrorが起こっているでし

出力2

a = 1
b = 0
ValueErrorかZeroDivisionErrorが起こっているでし

例外に名前を付ける

さらに、発生した例外に名前をつけることもできます。以下では、ValueErrorZeroDivisionErrorが発生するよう入力していますが、発生した例外を捕獲し、ERという名前をつけています。


try:
    a = int(input('a = '))
    b = int(input('b = '))
    print(a/b)
except (ValueError, ZeroDivisionError) as ER:
    print(f'{type(ER)}が起こっているでし')

出力1

a = 1.5
<class 'ValueError'>が起こっているでし

出力2

a = 1
b = 0
<class 'ZeroDivisionError'>が起こっているでし

このERという名前はexceptのスイート内でしか使えないので気をつけてください。次のようにするとNameErrorが発生してしまいます(せっかくなのでこのNameErrorも捕捉してやりましょう)。


try:
    a = int(input('a = '))
    b = int(input('b = '))
    print(a/b)
except (ValueError, ZeroDivisionError) as ER:
    pass

try:
    print(ER)
except NameError:
    print('ERって何のことでしか?')

出力

a = 1.5
ERって何のことでしか?

raiseで例外を発生させる

raiseによって意図的に例外を発生させることもできます。ただしここで使えるのはBaseExceptionクラスのサブクラスかインスタンスに限られます。(後述しますが、よく見る例外はほとんどこのBaseExceptionのサブクラスです。)


def ErFunc(b: int) -> None:
    if b == 0:
        raise ZeroDivisionError        
b = 0
try:
    ErFunc(b)
except BaseException as ER:
    print(f'{type(ER)}が発生してるでし')

出力

<class 'ZeroDivisionError'>が発生してるでし

ここではBaseExceptionクラスが指定されているので、その派生クラスは何でも捕捉できるようになっています。ここではZeroDivisionErrorが捕捉されています。

ユーザー定義の例外

最後に、クラスを使ってユーザー定義の例外クラスを作成することもできます。以下は一桁の二つの自然数の和を求めるプログラムですが、負の数や二桁の数が入力されたり、和が二桁になると例外を発生させるようにします。ParameterRangeExceptionクラスもReturnRangeExceptionクラスもどちらも範囲に関するクラスなので、RangeExceptionクラスから継承しています。もちろん、標準組み込みクラスであるExceptionクラスから継承するようにしても問題ありません。


class RangeException(Exception):
    pass

class ParameterRangeException(RangeException):
    pass

class ReturnRangeException(RangeException):
    pass

def add(a: int, b: int) -> int:

    if not 0 < a < 10:
        raise ParameterRangeException

    if not 0 < b < 10:
        raise ParameterRangeException

    if a + b > 10:
        raise ReturnRangeException
    else:
        return a + b

try:
    a = int(input())
    b = int(input())
    print(f'二数の和は{add(a, b)}でし')
except ParameterRangeException as PRE:
    print(f'{type(PRE)}が生じてるでし。仮引数は1~9じゃなきゃダメでし')
except ReturnRangeException as RRE:
    print(f'{type(RRE)}が生じてるでし。返却値を9以下にしなきゃダメでし')
finally:
    print('例外の説明は以上でし')

出力1

1
8
二数の和は9でし
例外の説明は以上でし

出力2

2
11
<class '__main__.ParameterRangeException'>が生じてるでし。仮引数は1~9じゃなきゃダメでし
例外の説明は以上でし

出力3

5
7
<class '__main__.ReturnRangeException'>が生じてるでし。返却値を9以下にしなきゃダメでし
例外の説明は以上でし

補足

  • Pythonに用意されている標準組み込み例外の親クラスはBaseExceptionクラスです。これを親として、Exceptionクラスがあり、これを継承した形でValueErrorクラスやIndexErrorクラスが存在しています。

  • ユーザー定義の例外を作る場合、UserExceptionClass()の引数にBaseExceptionを入れてはいけません。仕様的にBaseExceptionクラスはユーザー定義例外に継承されることを前提としていないようです。

  • BaseExceptionクラスの子クラスとしてArithmeticErrorクラスがありますが、このクラスの下にOverflowErrorクラスやZeroDivisionErrorクラスが存在します。もし算術エラーを扱いたいのであれば、ユーザー定義例外を作る際、引数にArithmeticErrorを入れても構いません(Exceptionクラスまたはそれよりも下部のクラスであれば何でもOK)。

5
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
7