次にPythonが標準で提供している組み込み例外について少し見ておこう。前回も触れたが、例外を表すクラスはさまざまに分類されて、継承階層を構成している。
その頂点にある(例外クラスの大本の基底クラスとなっている)のがBaseExceptionクラスだ。このクラスからはシステムの終了に関する例外クラスが幾つか派生しているが、それ以外にもう一つ重要なクラスが派生している。それがExceptionクラスだ。Exceptionクラスは、Pythonプログラムの実行時に発生するほぼ全ての例外を表す例外クラスの基底クラスとなる。
このような階層構成となっているのは、恐らくシステム終了(sysモジュールのexitメソッドによるPython環境の終了や、ユーザーのキーボード操作によるプログラムの中断)などの状況を、通常の例外とは別の継承階層に位置させることで、プログラム実行時に発生することが想定できる例外は「except:」節や「except Exception:」節で一挙に捕捉できるようにする一方で、システム終了という特殊な状況は別のexcept節(例外ハンドラ)で処理できるようにしたいからだろう。
try:
# 何かの処理
except ……:
# 個別の例外を処理する例外ハンドラ
except Exception as e:
# 個別に指定した以外の例外を一括して処理する例外ハンドラ
except:
# システム終了に関する例外(異常事態)を処理する例外ハンドラ
Pythonに組み込みの例外クラスを幾つか以下にまとめておこう。全ての例外クラスについてはPythonのドキュメント「組み込み例外」を参照されたい。以下の表ではクラス階層を「→」によるインデントで表している(例:ArithmeticErrorクラスの派生クラスにはZeroDivisionErrorクラスがある)。
例外クラス | 説明 |
---|---|
BaseException | 全ての例外クラスの基底クラス |
→Exception | システム終了に関連する例外クラス以外の組み込みクラスの基底クラス |
→AttributeError | オブジェクトの属性を参照しようとしたときに、その属性がなかったり、属性への代入が失敗したりすると発生する |
→ArithmeticError | 算術演算に関連する例外の基底クラス |
→→ZeroDivisionError | 0を除数として除算(割り算)を行ったときに発生する例外(例:「1 / 0」) |
→LookupError | リストや辞書などのインデックスやキーで存在しないものを指定した場合に発生する例外の基底クラス |
→→IndexError | リストなどで指定したインデックスに対応する要素がない場合に発生する例外(例:「'str'[4]」) |
→→KeyError | 辞書などで指定したキーに対応する値がない場合に発生する例外(例:「{}['key']」) |
→NameError | 指定した名前がローカルスコープやグローバルスコープに見つからなかったときに発生する例外(例:「print(undefine_var)」) |
→RecursionError | 関数の再帰呼び出しなどで、再帰の上限を超えたときに発生する例外 |
→StopIterationError | イテレータが返送する要素が尽きたときに、それ以降、イテレータから要素を取り出そうとすると発生する例外 |
→SyntaxError | 構文エラーの基底クラス |
→→IndentationError | ブロック内のコードのインデントが統一されていないと発生する例外 |
→→→TabError | ブロックのインデントにタブ文字と半角空白文字が混用されているときに発生する例外 |
→TypeError | 演算や操作の対象となったオブジェクトが、それをサポートしていないときに発生する例外(例:「1 + '2'」) |
→ValueError | 演算や操作の対象となったオブジェクトが型は合っているが、その値が適切でないときに発生するエラー(例:「int('one')」) |
Pythonに組み込みの例外クラス(抜粋) |
この表に示した以外にも多くのクラスがPythonでは標準で提供されている。多くの場合は、それらを使って、プログラムで発生した問題を表せるが、自分で独自の例外を定義できるとよいこともある。
例外クラスを独自に定義するときには注意する点が一つある。それは、他の例外クラスと同様に、Exceptionクラス(またはその派生クラス)を継承するクラスとすることだ。それを除けば、後は通常のクラスと同様だ。
例えば、先ほどのMyStackクラスでは、IndexError例外を送出していたが、その代わりにEmptyStackError例外を送出するようにしてみよう。これにはもちろんEmptyStackErrorクラスを定義する必要がある。なお、PEP 8では、例外クラスの最後には「Error」を付けることが推奨されている。そこで、ここでも最後に「Error」を付けるようにしている。
そのコードは簡単だ。
class EmptyStackError(Exception):
pass
一番簡単には、上のクラスを定義するだけで、独自の例外クラスを作成できる。そこで、先ほどのMyStackクラスも変更してみよう。
class EmptyStackError(Exception):
pass
class MyStack:
def __init__(self):
self.stack = []
def push(self, item):
self.stack.append(item)
def pop(self):
if len(self.stack) == 0:
raise EmptyStackError('stack is empty')
return self.stack.pop()
では、先ほどと同じコードでこれらのクラスの動作を確認してみよう。
mystack = MyStack()
mystack.push(0)
print(mystack.pop())
print(mystack.pop())
実行結果は次のようになる。
EmptyStackErrorクラスをカスタマイズするとしたら、インスタンス生成時に引数を渡さなくても「stack is empty」などのメッセージがインスタンス変数argsにセットされると便利かもしれない。これを行うには次のようなコードを書けばよい。
class EmptyStackError(Exception):
def __init__(self, *args):
if len(args) == 0: # 引数が渡されなかったら基底クラスの__init__
super().__init__('stack is empty') # メソッドにメッセージを渡す
else: # 引数が渡されたら全てを基底クラスの__init__メソッドに渡す
super().__init__(args)
こうすることで、以下のように「raise EmptyStackError」と書くだけで、「stack is empty」というメッセージ付きで例外が発生するようになる(既に述べたが、「raise」に続けて例外クラス名だけを書くと、引数なしで「例外クラス名()」呼び出しが行われる)。
class MyStack:
def __init__(self):
self.stack = []
def push(self, item):
self.stack.append(item)
def pop(self):
if len(self.stack) == 0:
raise EmptyStackError
return self.stack.pop()
実行結果は省略する。
今回は自分が書くコードから例外を送出する方法と、Pythonが標準で提供している例外クラスの概要、独自の例外クラスを定義する方法を見た。次回はファイル操作について見ていくことにしよう。
Copyright© Digital Advantage Corp. All Rights Reserved.