Python Tech

[初心者向け] Python if文でエラーを補足する?そこは「例外処理」にしましょう!

2021年6月2日

Pythonに慣れたい人向けに、基本のキから解説するシリーズ。

今回の記事では Pythonのコードで

  • エラーが発生してもそこでプログラムを終わらずに処理を続ける
  • エラーの種類ごとに処理を分ける

などを実現できる「例外処理」について現役のエンジニアが解説しています。

 

例外処理の基本

エラーの発生をとらえるには if 文ではなく、try-except文を使います。

他言語から来た人は 「try-catch」文などに馴染みのある人は多いでしょう。

 

使い方

例えば 0(ゼロ)で何かを割ることはできませんから、エラーが発生しますよね。

以下のようなコードではエラーになってプログラムが途中で終了してしまいます。

a = 1 / 0
print(a)
print("処理完了")
# ZeroDivisionError: division by zero

この例では1行目でエラーが発生し、次の行以降は実行されません。
(「処理完了」とは表示されませんね)

1行目で0で割ろうとすると、ZeroDivisionErrorという例外が発生しています。

このエラーを「キャッチ」して、処理を最後まで継続するためには例外処理を実装します。

a = None
try:
    a = 1 / 0
except ZeroDivisionError:
    print("ゼロ除算エラーが発生")

print("処理が完了")
print(a)

# ゼロ除算エラーが発生
# 処理が完了
# None

このように、処理をtry-except文で囲ってあげます。

except の後には例外名を書きます。

try:
    # 通常の処理
except:
    # 例外発生時の処理

こうすると、tryブロックの途中でZeroDivisionError例外が発生してもexcept文がキャッチし、
exceptブロックのコードが実行されます

「処理が完了」の行が実行されてることからも分かるように、たとえ例外が発生してもそのままプログラムが実行され続けています。

関数にすることで、もう少し実用的に書いてみましょう。

def div(a, b):
    result = None
    try:
        result = a / b
    except ZeroDivisionError as e:
        print("ゼロ除算エラーです")
        print(f"エラーメッセージ:{e}")
    return result

print(div(10, 5))
# 2

print(div(10, 0))
# ゼロ除算エラーです
# エラーメッセージ:division by zero
# None

except文はZeroDivisionError例外が発生したときにだけ実行され、例外が発生しても処理が続いている(return文でNoneが返ってきている)のが分かると思います。

 

すべての例外をキャッチする

上の例では0除算のエラー(ZeroDivisionError)の例外が発生したときはうまくキャッチできますが、他の例外には対応できません。

以下の例では、リスト(list)型の変数の範囲外にアクセスしようとして、IndexError例外を発生させます。

a = ['alpha', 'bravo', 'charlie']
target = None
try:
    target = a[10]
except IndexError as e:
    print("指定されたキーは範囲外です")
    print(f"エラーメッセージ:{e}")
print(target)

# 指定されたキーは範囲外です
# エラーメッセージ:list index out of range
# None

この場合、IndexError例外が発生するのが分かっていれば上のようなコードで大丈夫ですが、もし他の例外が起こった場合はプログラムが停止します。

すべての例外をキャッチしたいときは、以下のようにExceptionクラスを指定します。

try:
    target = a[10]
except Exception as e:
    print(f"エラーメッセージ:{e}")

# エラーメッセージ:list index out of range

Exceptionクラスは(ほとんど)すべての例外の既定クラスです。

そのため、どのような例外でもキャッチすることができます

どんな例外が発生しても必ず exceptブロックを実行させたいときや、想定できない例外が発生する可能性が高いときに使います。

ただしExceptionクラスのように、どのような例外もキャッチしてしまうと、プログラムの隠れたバグの原因になることも。後々にデバッグを難しくする原因にもなります。できるだけ具体的な例外クラスを指定するようにし、Exceptionを乱用するのは控えましょう。

例外を表すクラスはさまざまですが、Pythonの組み込みで指定されている例外は公式のドキュメントで確認できます。

 

複数の例外をキャッチする

では、具体的にいろいろな例外をキャッチしたいときは、どのようにコードを書けばいいのでしょう?

答えは単純で、以下のようにexcept文をいくつも指定すればOKです。

def get_item(l, d):
    target = None
    try:
        target = l[d]
    except IndexError as e:
        print("指定されたキーは範囲外です")
        print(f"エラーメッセージ:{e}")
    except TypeError as e:
        print("指定されたキーの形式は正しくありません")
        print(f"エラーメッセージ:{e}")
    return target

item = get_item([1, 2, 3], 99)
print(item)
# 指定されたキーは範囲外です
# エラーメッセージ:list index out of range
# None

item = get_item([1, 2, 3], 'a')
print(item)
# 指定されたキーの形式は正しくありません
# エラーメッセージ:list indices must be integers or slices, not str
# None

上のサンプルではIndexErrorとTypeErrorという2つの例外をそれぞれキャッチしているのが分かります。

もし、この2つのどちらでもない例外(想定できない)を捕捉したいときは、except Exceptionを追加してあげましょう。

 

例外がなかったときだけ実行する

「例外が発生したときの例外処理は分かったけど、例外が発生しなかったときだけ実行するコードはどう書けばいいの?」と思うかもしれません。

そんなときはelse文を使います。

elseはif文でも使いましたね。

def div_else(a, b):
    result = None
    try:
        result = a / b
    except ZeroDivisionError as e:
        print("ゼロ除算エラーです")
        print(f"エラーメッセージ:{e}")
    else:
        print("正常に完了")
    return result

print(div_else(1, 2))
# 正常に完了
# 0.5

print(div_else(1, 0))
# ゼロ除算エラーです
# エラーメッセージ:division by zero
# None

このサンプルでは、print(“正常に完了”) の文は例外が発生しなかったときにだけ実行されています。

try文の中で例外が起きなかったとき(つまりexcept文にキャッチされなかったとき)にだけelseブロックの中が実行されます。

 

例外に関わらず必ず実行する

また、例外が発生したかどうかに関わらず、絶対に実行したい処理があるとしましょう。

そんなときは finally 文を使います。

def div_fin(a, b):
    result = None
    try:
        result = a / b
    except ZeroDivisionError as e:
        print("ゼロ除算エラーです")
        print(f"エラーメッセージ:{e}")
    finally:
        print("最後に必ずする処理")
    return result

print(div_fin(1, 2))
# 最後に必ずする処理
# 0.5

print(div_fin(1, 0))
# ゼロ除算エラーです
# エラーメッセージ:division by zero
# 最後に必ずする処理
# None

上のサンプルでは、例外に関わらず print(“最後に必ずする処理”) の文が実行されています。

finally文は、メインとなる処理が終わったあとに、後処理をするときによく使われます。例えば、データベースとの接続を終了したり、ファイルに書き込みを行ったりなどです。

 

どんなときに例外処理を書くの?

Pythonに限りませんが、プログラミングをしていく上で例外処理は避けては通れないくらい重要です。

上で見たように、0で割り算をしたり範囲外の値を取得しようとしたりする(重大なバグのある)コード以外でも、頻繁に例外と向き合うはずです。

例えば、以下のようなエラーはとても一般的です

  • 整数を入力してほしいフォームに、ユーザーが文字列を入力した
  • インターネットからファイルをダウンロードする処理で、ファイルがすでに削除されていた
  • 処理途中のファイルが消されてしまった

このような例外が発生するたびにプログラムが強制終了していては、とんでもなく不便なプログラムになってしまいます。

そのため、例外が起きてもプログラムを途中で止めたくないときは、例外処理をきちんと実装する必要があるのです。

 

まとめ

Pythonでエラーが発生したときに処理を止めないためには、if文ではなく、try-except文の例外処理を実装することを確認しました。

  • この記事を書いた人

次世代ペンギン

長いのでペンギンとお呼びください。システム開発・プログラミングのお仕事をしています。甘味とコーヒーは生命線。日常での学びを記事にしています。

人気の記事

1

キャリアアップのため、または高収入を目指して、しっかりプログラミングを学びたいという人が増えてきましたね。 この記事では現役のエンジニアである私が、実際に仕事で稼げるようになるためのスクール選びで失敗 ...

2

先日の記事では、初心者からフリーランスプログラマーになる難しさと、それでもなんとか稼げるようになるにはどうしたらいいか解説しました。 今回の記事では、稼げるフリーランサーになるために必要な要素を、もう ...

3

気休めだけの甘い言葉は書きません。 最近は多くの企業やサイトで、使い捨てられるプログラマーが欲しいがために、甘い言葉で初心者プログラマを誘い出しています。 この記事では、まずは「現実」をちゃんと理解し ...

4

Vuetifyの v-progress-circular コンポーネントは、数値データや処理状況を環状(円状)のデザインで教えてくれるUIデザインです。 ローディングのスピナー(処理中のマーク)として ...

5

※画像はずとまよの新曲とは一切関係ありません   ずっと真夜中でいいのに。(以下、ずとまよ)の新曲『勘ぐれい』のMVが12月1日に公開されましたね。 MV前に公開されていた原曲を聴き、神曲な ...

6

Vuexのstore(ストア)を使うと、各コンポーネント間で個別にデータのやり取りすることなく、データを一元的に管理できます。Vueでは欠かせない機能といえるでしょう。 state(変数、コンポーネン ...

-Python, Tech
-,

© 2021 ペンギンのーと