開発言語 Python(その13)

【連載】

にわか管理者のためのLinux運用入門

【第174回】開発言語 Python(その13)

[2019/04/16 15:25]後藤大地 ブックマーク ブックマーク

サーバ/ストレージ

例外

数値は0で割ることはできないし、文字列が想定されている部分に数字が指定されればエラーになる。次のように不適切な記述をすれば、Pythonインタプリタからエラーが報告される。

>>> 10 * (1/0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> 4 + spam*3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: must be str, not int
>>>

Pythonではこうしたエラーに対し、プログラムで対処することができる。比較的新しいプログラミング言語が例外処理を備えているように、Pythonにも例外処理の機能が備わっているためだ。例えば次のように「try-except制御構文」を記述すると、「10 * (1/0)」で発生したエラーが「except ZeroDivisionError:」以降で捕捉できていることがわかるだろう。これが例外処理の基本的な書き方だ。

>>> try:
...     10 * (1/0)
... except ZeroDivisionError:
...     print("except clause")
... 
except clause
>>>

The Python Tutorial」には、例外処理の基本的な書き方として次のようなサンプルが掲載されている。

>>> while True:
...     try:
...         x = int(input("Please enter a number: "))
...         break
...     except ValueError:
...         print("Oops!  That was no valid number.  Try again...")
... 
Please enter a number: a
Oops!  That was no valid number.  Try again...
Please enter a number: b
Oops!  That was no valid number.  Try again...
Please enter a number: c
Oops!  That was no valid number.  Try again...
Please enter a number: あ
Oops!  That was no valid number.  Try again...
Please enter a number: い
Oops!  That was no valid number.  Try again...
Please enter a number: 10.19eadd
Oops!  That was no valid number.  Try again...
Please enter a number: 10.19
Oops!  That was no valid number.  Try again...
Please enter a number: 10
>>>

上記サンプルでは、標準入力から入力されたデータが整数以外の場合にエラーが発生するため、そのたびに「except ValueError:」へ処理が飛ぶようになっている。while文の中にあるため、「except ValueError:」の処理が終わると「while:」の初めに処理が戻る。こうして整数が入力されるまで同じ処理が繰り返され、整数が入力されるとbreakが実行されて処理が終了するという仕組みになっている。

ここで、例外が発生した場合には「break」まで処理が移動していないことに注目しておきたい。例外が発生すると即座に対応する「except」に処理が移っているわけである。

「except」に対応するエラーが存在しない場合、次のようにデフォルトの例外処理が行われ、処理が終了してしまう。

>>> while True:
...     try:
...         x = int(input("Please enter a number: "))
...         break
...     except NameError:
...         print("Oops!  That was no valid number.  Try again...")
... 
Please enter a number: a
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
ValueError: invalid literal for int() with base 10: 'a'
>>>

exceptには何も指定しないという書き方も可能だ。その場合、例外が発生すると次のようにexceptがワイルドカード的に全ての例外に一致するようになる。

>>> while True:
...     try:
...         x = int(input("Please enter a number: "))
...         break
...     except:
...         print("Oops!  That was no valid number.  Try again...")
... 
Please enter a number: a
Oops!  That was no valid number.  Try again...
Please enter a number: b
Oops!  That was no valid number.  Try again...
Please enter a number: c
Oops!  That was no valid number.  Try again...
Please enter a number: 10,12x
Oops!  That was no valid number.  Try again...
Please enter a number: 10.11
Oops!  That was no valid number.  Try again...
Please enter a number: 10
>>>

exceptは複数個書いておくことができるので、exceptだけの項目は最後に書くことになる。ここに想定外の例外が発生した場合の処理を記述しておくのである。

exceptの処理では、発生した例外そのものや、例外に指定された引数にアクセスしなければならないケースもある。その場合には、次のように「as」を使って例外にアクセスできるようにして使用すればよい。

>>> try:
...     raise Exception('spam', 'eggs')
... except Exception as inst:
...     print(type(inst))    # the exception instance
...     print(inst.args)     # arguments stored in .args
...     print(inst)          # __str__ allows args to be printed directly,
...                          # but may be overridden in exception subclasses
...     x, y = inst.args     # unpack args
...     print('x =', x)
...     print('y =', y)
... 
<class 'Exception'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs
>>>

elseとfinally

Pythonの例外処理では「except:」に関して「else」と「finally」という指定もできるようになっている。elseは次のようにexceptの最後に加えて使用するもので、用意したexceptが全て一致しなかった場合に処理が実施される。だが、次の例のように、例外が発生しなかった場合にもelseが実行されるという点に注目していただきたい。この辺りがexceptに何も指定しなかった場合と処理が異なっている。

>>> while True:
...     try:
...         x = int(input("Please enter a number: "))
...     except ValueError:
...         print("Oops!  That was no valid number.  Try again...")
...     else:
...         print("else clause")
...         break
... 
Please enter a number: a
Oops!  That was no valid number.  Try again...
Please enter a number: b
Oops!  That was no valid number.  Try again...
Please enter a number: c
Oops!  That was no valid number.  Try again...
Please enter a number: 1
else clause
>>>

finallyはelseとは違い、何があっても必ず実行される項目だ。次のサンプルを見てもらえれば、例外が発生した場合でも、発生しなかった場合でも、breakが実行されても、finallyの内容が処理されてることがわかる。

>>> while True:
...     try:
...         x = int(input("Please enter a number: "))
...         break
...     except ValueError:
...         print("Oops!  That was no valid number.  Try again...")
...     finally:
...         print("finally clause")
... 
Please enter a number: a
Oops!  That was no valid number.  Try again...
finally clause
Please enter a number: b
Oops!  That was no valid number.  Try again...
finally clause
Please enter a number: c
Oops!  That was no valid number.  Try again...
finally clause
Please enter a number: 1
finally clause
>>>

elseとfinallyは共存できる。例えば、次のような使い方だ。

>>> while True:
...     try:
...         x = int(input("Please enter a number: "))
...     except ValueError:
...         print("Oops!  That was no valid number.  Try again...")
...     else:
...         print("else clause")
...     finally:
...         print("finally clause")
... 
Please enter a number: a
Oops!  That was no valid number.  Try again...
finally clause
Please enter a number: b
Oops!  That was no valid number.  Try again...
finally clause
Please enter a number: c
Oops!  That was no valid number.  Try again...
finally clause
Please enter a number: d
Oops!  That was no valid number.  Try again...
finally clause
Please enter a number: 
Oops!  That was no valid number.  Try again...
finally clause
Please enter a number: 1
else clause
finally clause
Please enter a number:

finallyは主に「リソースを閉じる」といったような、エラーが発生してもしなくても処理すべき内容を書いておくケースで使われることが多い。

例外とクラス

exceptには例外ではなくクラスを指定することもできる。その場合、次のように動作する。一致したexceptが実行されていることがわかるだろう。

>>> class B(Exception):
...     pass
... 
>>> class C(B):
...     pass
... 
>>> class D(C):
...     pass
... 
>>> for cls in [B, C, D]:
...     try:
...         raise cls()
...     except D:
...         print("D")
...     except C:
...         print("C")
...     except B:
...         print("B")
... 
B
C
D
>>>

ここで、記述の順番を逆にしてExceptionから派生したBをCやDのexceptよりも前に記述すると、今度はBしか一致しなくなる。BがExceptionから派生したクラスであり、ここが先に一致するためだ。

>>> for cls in [B, C, D]:
...     try:
...         raise cls()
...     except B:
...         print("B")
...     except C:
...         print("C")
...     except D:
...         print("D")
... 
B
B
B
>>>

例外は便利な機能だが、どの項目が一致するかはよく把握しておく必要がある。勘違いした状態でコーディングすると、問題が発生した場合の処理を適切に実施できなくなる。

例外処理はかならず書くべきか?

読者の中には、ツールとしてPythonを使いたいだけで、それほどエラー処理をしっかり書くつもりはないという方もおられるだろう。それはそれでよいと思うし、むしろそちらのほうが本連載の趣旨には合致する。

確かに、例外処理をきっちり書いておけば、さまざまなケースに対応できるコードに仕上げられる。ただし、処理がポンポン飛ぶことになるのでコーディングが難しくなるのは否めない。本連載では、あくまでもPythonは日々の業務を楽にするためのツールだととらえており、複雑な処理をコーディングするために使おうとはしていないので、他人の書いたコードが読める程度に機能を知っておいてもらえればと思う。

【参考資料】
The Python Tutorial

※ 本記事は掲載時点の情報であり、最新のものとは異なる場合がございます。予めご了承ください。

一覧はこちら

連載目次

もっと知りたい!こちらもオススメ

なぜ今、統合システムなのか? 押さえておくべき「3つのインパクト」

なぜ今、統合システムなのか? 押さえておくべき「3つのインパクト」

ガートナー ジャパンは10月31日~11月2日、都内で「Gartner Symposium/ITxpo 2017」を開催。11月1日には同社 主席アナリストの青山浩子氏が登壇し「CIOが理解すべき統合システムの3大インパクト」と題する講演を行った。本稿では、講演の内容をダイジェストでお届けする。

関連リンク

この記事に興味を持ったら"いいね!"を Click
Facebook で IT Search+ の人気記事をお届けします

会員登録(無料)

注目の特集/連載
知りたい! カナコさん 皆で話そうAIのコト
教えてカナコさん! これならわかるAI入門
対話システムをつくろう! Python超入門
Kubernetes入門
AWSで作るクラウドネイティブアプリケーションの基本
ソフトウェア開発自動化入門
PowerShell Core入門
徹底研究! ハイブリッドクラウド
[解説動画] Googleアナリティクス入門講座
マイナビニュース スペシャルセミナー 講演レポート/当日講演資料 まとめ
セキュリティアワード特設ページ

一覧はこちら

今注目のIT用語の意味を事典でチェック!

一覧はこちら

ページの先頭に戻る