1.2.8. Python での例外処理

これまでの全てのコマンドを打ち込んでいれば、何度か例外送出を経験しているでしょう。例えばコマンドを打ち間違えると例外が送出されます。

Python コードを実行するといろいろな種類のエラーにより例外が送出されます. ソースコードの中では例外を捉えたり, カスタマイズしたエラー型を定義できます. 正しい例外型を探しているときには、the built-in Exceptions の記述に目を通すといいでしょう。

1.2.8.1. 例外

Python ではエラーは例外を送出します:

In [1]: 1/0
---------------------------------------------------------------------------
ZeroDivisionError: integer division or modulo by zero
In [2]: 1 + 'e'
---------------------------------------------------------------------------
TypeError: unsupported operand type(s) for +: 'int' and 'str'
In [3]: d = {1:1, 2:2}
In [4]: d[3]
---------------------------------------------------------------------------
KeyError: 3
In [5]: l = [1, 2, 3]
In [6]: l[4]
---------------------------------------------------------------------------
IndexError: list index out of range
In [7]: l.foobar
---------------------------------------------------------------------------
AttributeError: 'list' object has no attribute 'foobar'

ご覧の通り、異なるエラーは 異なる例外型 を送出します.

1.2.8.2. 例外を捉える

1.2.8.2.1. try/except

In [10]: while True:
....: try:
....: x = int(raw_input('Please enter a number: '))
....: break
....: except ValueError:
....: print('That was no valid number. Try again...')
....:
Please enter a number: a
That was no valid number. Try again...
Please enter a number: 1
In [9]: x
Out[9]: 1

1.2.8.2.2. try/finally

In [10]: try:
....: x = int(raw_input('Please enter a number: '))
....: finally:
....: print('Thank you for your input')
....:
....:
Please enter a number: a
Thank you for your input
---------------------------------------------------------------------------
ValueError: invalid literal for int() with base 10: 'a'

リソース管理で重要(例 ファイルを閉じる)

1.2.8.2.3. 許可を得るより謝る方が簡単

In [11]: def print_sorted(collection):
....: try:
....: collection.sort()
....: except AttributeError:
....: pass
....: print(collection)
....:
....:
In [12]: print_sorted([1, 3, 2])
[1, 2, 3]
In [13]: print_sorted(set((1, 3, 2)))
set([1, 2, 3])
In [14]: print_sorted('132')
132

1.2.8.3. 例外を送出する

  • 例外を捉えて再送出する:

    In [15]: def filter_name(name):
    
    ....: try:
    ....: name = name.encode('ascii')
    ....: except UnicodeError, e:
    ....: if name == 'Gaël':
    ....: print('OK, Gaël')
    ....: else:
    ....: raise e
    ....: return name
    ....:
    In [16]: filter_name('Gaël')
    OK, Gaël
    Out[16]: 'Ga\xc3\xabl'
    In [17]: filter_name('Stéfan')
    ---------------------------------------------------------------------------
    UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 2: ordinal not in range(128)
  • コードのある部分をパスするための例外:

    In [17]: def achilles_arrow(x):
    
    ....: if abs(x - 1) < 1e-3:
    ....: raise StopIteration
    ....: x = 1 - (1-x)/2.
    ....: return x
    ....:
    In [18]: x = 0
    In [19]: while True:
    ....: try:
    ....: x = achilles_arrow(x)
    ....: except StopIteration:
    ....: break
    ....:
    ....:
    In [20]: x
    Out[20]: 0.9990234375

特定の状況に遭遇した(例 StopIteration)ことや特定の状況に遭遇していない状況 (例 カスタマイズしたエラー)を通知するために例外を使いましょう