Pythonのtry文とexceptによる例外処理の方法

try文とexcept節は、特定のエラー(例外)が発生した時の処理を指定する構文です。ここでは、この基本的な使い方や「例外」というものを詳しく解説した上で、elseやfinallyを組み込んだ時の分岐についても触れていきます。

try文とexcept節をマスターするために役立てて頂ければ嬉しく思います。

目次

1. try文とexcept(例外処理)とは

try exceptは「特定のエラー(例外)が発生した時は指定の処理を行う」というコードを書くときに使うプログラミング構文です。

コードを書いていると数多くのエラーを経験しますが、エラーは大きく2つに明確に分けられています。

  • 構文エラー(SyntaxError):そもそも文法が間違えているためにプログラムが動かないエラー
  • 例外(Exception):プログラムの実行中に発生するエラー

前者の構文エラーは、そもそもコードの書き方を間違えているので正しいものに書き直さなければ動作することはできません。一方で、後者の例外の場合は、一度プログラムは動作しており途中でエラーが発生しているという状態です。

そして例外に対しては、それが発生した時にどのような処理をするのかを指定しておくことができます。そのために使うのがtry文とexceptです。例外については後ですぐに詳しく解説するとして、まずはtry文とexceptの書き方を見てみましょう。

1.1. try文とexceptの書き方

次のように書きます。

In [ ]:
try:
    処理文
except:
    処理文

tryブロックに書いているコードで例外が発生したら、exceptブロックに書いている処理が実行されます。これを書くと処理の流れは下図のようになります。

実際のコードを見てみましょう。例えばPythonでは、数値を0で割ろうとすると、次のようにZeroDivisionErrorという例外が発生します。

In [1]:
num = 2/0
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-1-415392448056> in <module>
----> 1 num = 2/0

ZeroDivisionError: division by zero

赤字で示しているようにPythonは例外が発生した時、その種類を表示してくれます。それでは、これに例外処理を指定してみましょう。次のコードをご覧ください。

In [2]:
try:
    num = 2/0
except:
    print('数値を0で割ることはできません。計算式を直しましょう。')
数値を0で割ることはできません。計算式を直しましょう。

このように、まずtryブロックに実行する処理を書きます。その後にexceptブロックで、tryブロックに書いた処理に例外が発生した場合の処理を書きます。結果、エラーメッセージは発生せずに、exceptブロックに書いた処理が実行されていますね。

これが基本的なtry文とexcept節の使い方ですが、これには注意点があります。それはexcept節を上述のように使うと、全ての例外をキャッチしてしまうため、通常のプログラムエラーを全て同じように処理してしまい、どのような例外が発生しているのかが分からなくなってしまうからです。

そのため、これから見るようにexcept節では、どの例外に対して処理を行うのかを指定するのが普通です。これから解説しますが、その第一歩として、Pythonで発生する例外の種類について確認しておきましょう。

1.2. 例外の種類について

それでは「例外」について、もう少し詳しく見ていきましょう。Pythonでよく見る代表的な例外には次の5つがあります。

  • NameError
  • ZeroDivisionError
  • TypeError
  • ValueError
  • IndexError

もちろん、これだけではありませんが、ここで解説する基本を理解しておくと、どのような例外にも対処できるようになります。

それぞれ確認しておきましょう。

1.2.1. NameError

NameErrorは、値が定義されていない変数を使った場合に発生する例外です。以下をご覧ください。

In [1]:
a = 10
a/b
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-1-e26af6e178a4> in <module>()
      1 a = 10
----> 2 a/b

NameError: name 'b' is not defined

変数aには数値の10という値を持っていますが、変数bは何も値を持っていません。この場合、10を割る数値がないので、a/bという計算を行っても例外が発生します。

1.2.2. ZeroDivisionError

ZeroDivisionErrorは、数値を0で割った時の発生する例外です。以下をご覧ください。

In [2]:
a =10
b = 0
a/b
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-2-d48c99de26f4> in <module>()
      1 b = 0
----> 2 a/b

ZeroDivisionError: division by zero

0での割り算は不可能なので、このエラーが発生します。

1.2.3. TypeError

TypeErrorとは型が異なるオブジェクト同士を処理しようとする時に発生する例外です。型については「Pythonの型とは」で解説しています。それでは以下をご覧ください。

In [3]:
sum = '1' + 1
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-1-42f78b265985> in <module>
----> 1 sum = '1' + 1

TypeError: can only concatenate str (not "int") to str

「’1’」は文字列型オブジェクトで、「1」は整数型オブジェクトなので、両者を連結することはできません。そのためTypeErrorが発生します。

1.2.4. ValueError

ValueErrorは、型変換ができなかった時に発生する例外です。型については「Pythonの型とは」をご覧ください。それでは以下をご覧ください。

In [4]:
num = int("100個")
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-3-e81978e23ff3> in <module>()
----> 1 num = int("100個")
ValueError: invalid literal for int() with base 10: '100個'

このコードで使っているint()は、オブジェクトの型を整数に変換する関数です。詳しくは、「Pythonの数値と文字列を変換する方法」で解説しています。通常は、文字列を整数に変換するために使います。

上のコードでは、int(“100個”) と入力していますね。文字列の ‘100’ を数値の 100 に変換することは可能ですが、”個”という漢字を整数に変換することはできません。そのためにValueErrorが発生します。

1.2.5. IndexError

IndexErrorは、文字列やリストから、存在しない値を取り出そうとした時に発生するエラーです。値を取り出す方法は、「Pythonの文字列を抽出する方法」や「Pythonのリストから要素を取り出す方法」でご確認ください。それでは以下のコードをご覧ください。

In [5]:
nums = []
nums.pop()
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-4-3f50d14777dc> in <module>()
      1 nums = []
----> 2 nums.pop()
IndexError: pop from empty list

pop()は、リストやタプルから、要素を削除するメソッドです(「Pythonのリストの要素を削除する方法まとめ」で解説しています)。しかし、この例では、リストが空で、そもそも要素が存在しません。削除対象の要素がないので、IndexErrorが発生しています。

1.3. 「except 例外名:」で例外ごとに処理を指定

さて、Pythonではどのような例外が発生するのか、ある程度理解できたところでもう一度try文とexceptについて見ていきましょう。try文とexceptでは、「except 例外名」と書くことで、例外ごとに処理を指定することができます。こちらの書き方の方が使いますので、しっかり抑えておきましょう。

以下のコードをご覧ください。

In [1]:
try:
    print(a)
except NameError:
    print('変数aの値が定義されていません。')
except ZeroDivisionError:
    print('0での割り算は行えません。')
except TypeError:
    print('文字列と数値は連結できません。')
except ValueError:
    print('型変換が不正です。')
変数aの値が定義されていません。

まず、このコードは下図のような流れになっています。tryブロックで実行している処理がprint(a)で、変数aに値が存在しないのでNameErrorとなり「except NameError:」ブロックの処理が実行されています。

以下のコードでは、tryブロックの処理で発生している例外が、ZeroDivisionErrorなので、「except ZeroDivisionError:」ブロックの処理が実行されています。

In [2]:
try:
    num = 2/0
except NameError:
    print('変数aの値が定義されていません。')
except ZeroDivisionError:
    print('0での割り算は行えません。')
except TypeError:
    print('文字列と数値は連結できません。')
except ValueError:
    print('型変換が不正です。')
0での割り算は行えません。

except節で指定していない例外が発生した場合は、そのままエラーメッセージが出ます。次のコードでは、except節で指定していないIndexErrorが発生しているのでエラーメッセージになっていますね。

In [3]:
nums = []

try:
    nums.pop()
except NameError:
    print('変数aの値が定義されていません。')
except ZeroDivisionError:
    print('0での割り算は行えません。')
except TypeError:
    print('文字列と数値は連結できません。')
except ValueError:
    print('型変換が不正です。')
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-9-c7ad30943f37> in <module>
      2 
      3 try:
----> 4     nums.pop()
      5 except NameError:
      6     print('変数aの値が定義されていません。')

IndexError: pop from empty list

複数の例外に対して同じ処理を指定したい時は次のように書きます。括弧が必須ですので忘れないようにしましょう。

In [4]:
try:
    sum = '1'  + 1
except NameError:
    print('変数aの値が定義されていません。')
except ZeroDivisionError:
    print('0での割り算は行えません。')
except (TypeError, ValueError):  #  ←TypeErrorとValueErrorに同じ例外処理を指定
    print('型に問題があります。')
型に問題があります。

1.4. 「except 例外名 as 変数名:」でエラーを格納

さらに「except 例外名 as 変数名:」と書くことで、変数に例外を格納することができます。この場合の変数名には’error’を表す e か err を使うのが通例です。

これを使うことで次のように想定した例外が発生しているかどうかを確認することができます。

In [1]:
try:
    2/0
except ZeroDivisionError as e:  #  ←変数にエラーオブジェクトを代入。
    print(e)  #  ←  変数をexceptブロックで出力して内容を確認できる。
    print(type(e))
division by zero
<class 'ZeroDivisionError'>

なお、この場合は、except節でZeroDivisionErrorを変数eに入れるように指定しているので、もし実際に発生したのがTypeErrorなどの他の例外だった場合は、エラーメッセージが表示されて処理が中断します。

1.5. 処理文に「pass」と書くと何も行わない

処理文にpassと書くと、例外が発生しても何も行わずにスルーします。以下のコードでは、「except ZeroDivisionError:」のブロックの処理文に「pass」と書いているので、その例外が発生した時は何も行われずにスルーされていますね。

In [1]:
try:
    2/0
except NameError:
    print('変数aの値が定義されていません。')
except ZeroDivisionError:
    pass  # ← ZeroDivisionErrorの時は何も行わない
except TypeError:
    print('文字列と数値は連結できません。')
except ValueError:
    print('型変換が不正です。')

2. try文の分岐(elseとfinally)

以上がtry文とexceptの基本です。ここからは、try文で使える分岐について見ていきましょう。

2.1. elseで例外が発生しなかった時の処理を指定

続いて、try文にelseブロックを追加すると、例外が発生しなかった時のみに実行する処理を指定することができます。

次のように書きます。

In [ ]:
try:
    処理文
except:
    処理文
else:
    処理文

実際に確認してみましょう。次のコードでは、例外は発生しなかったのでelseブロックの処理が実行されています。

In [1]:
try:
    2/1
except ZeroDivisionError:
    print('数値を0で割ることはできません。')
else:
    print('例外は発生しませんでした。')
例外は発生しませんでした。

一方、次のコードでは例外が発生したのでexceptブロックの処理が実行されて、elseブロックの処理は実行されていませんね。

In [2]:
try:
    2/0
except ZeroDivisionError:
    print('数値を0で割ることはできません。')
else:
    print('例外は発生しませんでした。')
数値を0で割ることはできません。

Pythonのtry文のexcept elseで例外が発生しなかった時の処理を書く」でも解説しているので、ぜひあわせてご確認ください。

2.2. finallyでtry文終了時に行う処理を指定

try文にfinallyブロックを追加すると、下図のように、エラーが発生した時としなかった時のどちらの場合でも、最後に行う処理を追加することができます。

次のように書きます。

In [ ]:
try:
    処理文
except:
    処理文
finally:
    処理文

例を見てみましょう。以下のコードでは、exceptブロックの例外処理も、finallyブロックの処理も実行されていますね。

In [1]:
try:
    2/0
except ZeroDivisionError:
    print('数値を0で割ることはできません。')
finally:
    print('try文を終了します。')
数値を0で割ることはできません。
try文を終了します。

Pythonのtry except finallyで例外発生の有無を問わず行う処理を書く」もあわせてご確認ください。

2.3. elseとfinallyを同時に使う

finallyとelseは同時に指定することもできます。その場合、次のような流れになります。例外が発生しなかった場合はelseブロックとfinallyブロックが実行され、例外が発生した時はexceptブロックとfinallyブロックが実行されます。

実際のコードを見てみましょう。以下のコードでは、例外が発生しなかったのでelseブロックの処理とfinallyブロックの処理が実行されています。

In [2]:
try:
    2/1
except ZeroDivisionError:
    print('数値を0で割ることはできません。')
else:
    print('例外は発生しませんでした。')
finally:
    print('try文を終了します。')
例外は発生しませんでした。
try文を終了します。

例外が発生した場合は、以下のようにexceptブロックの処理とfinallyブロックの処理が実行されます。

In [3]:
try:
    2/0
except ZeroDivisionError:
    print('数値を0で割ることはできません。')
else:
    print('例外は発生しませんでした。')
finally:
    print('try文を終了します。')
数値を0で割ることはできません。
try文を終了します。

3. まとめ

ここまで見てきたように、try文とexceptは例外が発生した時の処理を指定するものです。どのような例外が発生しうるのかをしっかりと想定して、基本的には「except 例外名:」の書き方を使うようにしましょう。例外名を指定しない方法では、想定外の例外に対しても処理が実行されてしまい、エラー内容がわからなくなってしまいます。

この点に注意して、使いこなしていきましょう。



よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

コメント

コメントする

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

目次
閉じる