[Python入門]例外の送出と例外クラス:Python入門(1/2 ページ)
自分のコードから例外を発生させる(送出する)方法と、Pythonが標準で提供する例外クラスの概要、独自に例外クラスを定義する方法を見ていく。
前回はPythonの例外、それを処理する方法について見た。今回は自分が書いたコードから例外を、自分のコードの利用者(とは自分かもしれないし、別の誰かかもしれない)に向けて送り出す方法や、Pythonに標準で組み込みの例外クラスの概要、例外クラスを独自に定義する方法を説明する。
例外の送出
前回は主に例外を処理する基本について見たが、例外はraise文を使って発生させることも可能だ。これを「例外を送出する」などと表現することもある。自分で関数やメソッドを定義しているときに、パラメーターに受け取った値が想定したものとは異なるなど、それらの関数やメソッドで処理を続行させることが不可能なときに例外を送出することで、呼び出した側に異常事態の発生を知らせることができる。
第30回「クラスを使ってスタックとキューを作成する」では以下に示すスタックを作成した。
class MyStack:
def __init__(self):
self.stack = []
def push(self, item):
self.stack.append(item)
def pop(self):
if len(self.stack) == 0:
return None
return self.stack.pop()
スタックに要素がないときにこのようにNone値を返すのも一つの手だが、例外を送出して、スタックの利用者に異常事態が発生したことを通知する方法もある。前もって要素数を調べたりせずに、「self.stack.pop()」行を実行して、スタックに要素がなければ、そこから発生する例外をそのまま呼び出し側に渡してしまっても構わないが、以下ではpopメソッドで要素の有無を調べて、要素がなければ例外を送出するようにしてみよう。
raise文
例外を送出するにはraise文を使用する。その基本構文を以下に示す(詳細な構文についてはPythonのドキュメント「raise文」を参照されたい)。
raise 例外クラスのインスタンス
「例外クラスのインスタンス」という部分には、例外クラスの「クラス名()」という呼び出しを書いて新規に例外クラスのインスタンスを生成するか、単に「クラス名」を角だけでもよい。後者の場合は、指定されたクラスのインスタンスが自動的に生成される。前者の場合には、関数やメソッドを呼び出した側に伝えるメッセージを引数として渡せる。
ちなみに空のリストに対してポップ操作を実行しようとしたら、どうなるだろうか。
[].pop()
これを実行すると、次のように例外が発生する。
発生した例外はIndexError例外で、そのメッセージは「pop from empty list」となっている。上に示したMyStackクラスであり、確かに内部ではリストを使っているので「pop from empty list」というメッセージでも問題はないかもしれない(つまり、自分で例外を送出せずにlistクラスが持つpopメソッドに全てを任せても問題ないという判断も可能だ)。だが、ここではIndexError例外を送出して、「スタックが空である」ことを意味するメッセージを渡してみよう。
以上をコードにまとめると次のようになる。
class MyStack:
def __init__(self):
self.stack = []
def push(self, item):
self.stack.append(item)
def pop(self):
if len(self.stack) == 0:
raise IndexError('stack is empty')
return self.stack.pop()
上のコードでは「return None」としていたところを、「raise IndexError('stack is empty')」として、「スタックが空である」ことを伝えるIndexError例外を送出するようにしている。なお、ここで渡した情報は全ての例外が共通に持っているインスタンス変数argsにタプルとして渡される(例外クラスのインスタンス生成時に、複数の引数を渡せば、それらが全てタプルにまとめられる。そのため、例外を処理する側で必要になりそうな情報を必要なだけ渡せるようになっているということだ)。
では、実際にこのクラスのインスタンスを生成して、使ってみよう。
mystack = MyStack()
mystack.push(0)
print(mystack.pop())
print(mystack.pop())
実行結果を以下に示す。
スタックにプッシュした「0」がpopメソッドで取り出された後、2つ目のpopメソッド呼び出しで例外が発生したことが分かる。そして、エラーメッセージにも「stack is empty」と表示されているので、MyStackクラスが例外を発生するクラスとなったことが確認できた。
コードや仕様の変更が及ぼす影響
例外の送出や例外処理と直接関係あるわけではないが、注意することが一つある。上のコードは動作確認程度のコードだったのであまり問題はないが、実際にこのクラスを使っている人がいるとしたら、使用している側のコードにも変更が必要となるのだ。以前のMyStackクラスの振る舞いは「スタックが空のときにpopメソッドを呼び出すとNone値が返される」というものだった。つまり、以下のようなコードを書いていたはずだ。
mystack = MyStack()
mystack.push(……)
# スタックを使うコードやその他のコード
item = mystack.pop()
if item != None:
print(item) # スタックからポップした要素を使った処理
しかし、今度は「スタックが空のときにpopメソッドを呼び出すと例外が発生する」ようになった。つまり、コードの変更(MyStackクラスの仕様の変更)によって、それを使用する側でもコードを変更する必要が出たということだ。例えば、上のコードならif文ではなくtry文を使用して、次のようなコードにすることが考えられる。
mystack = MyStack()
mystack.push(……)
# スタックを使うコードやその他のコード
try:
item = mystack.pop()
except IndexError as e:
print(e) # 例外を処理
else:
print(item) # スタックからポップした要素を使った処理
ここでは、「print(e)」として、IndexErrorクラスのインスタンス生成時に渡された値を表示するだけだ(興味のある方は「print(e.args)」としてみよう。上のメッセージを要素としたタプルが表示されるはずだ)。
自分だけが使うクラスなら今行ったような変更も許容できるかもしれないが、他の人にも使ってもらうコードを書いているときには、そのコードの振る舞いをどうすべきかは慎重に検討して、一度固まった仕様はよほどのことがない限り、変更せずに済むようにした方がよいだろう。
Copyright© Digital Advantage Corp. All Rights Reserved.