【Python】エラー処理と例外処理

【Python】エラー処理と例外処理 プログラミング

「try文がイマイチよくわからない・・・」
「例外処理とか難しそう。。。」
「Pythonでのエラー処理を理解したい」

このような場合には、この記事の内容が役に立つでしょう。
この記事では、Pythonにおけるエラー処理と例外処理について説明しています。

本記事の内容

  • Pythonにおけるエラーとは?
  • Pythonにおける例外処理
  • 例外をスタックトレースで確認

それでは、上記に沿って解説していきます。

Pythonにおけるエラーとは?

Pythonにおけるエラーとは、次の2種類に分けることができます。

  • 構文エラー (syntax error)
  • 例外 (exception)

これは、Pythonだけに限らずプログラム言語全般に言えることでしょう。

構文エラー (syntax error)

構文エラーは、構文解析エラー (parsing error) としても知られています。
例えば、次のコードを実行した場合に構文エラーが発生します。

print "test"

表示されるエラーは、以下。

    print "test"
    ^^^^^^^^^^^^
SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)?

PHP脳でコーディングすると、例のようにprintを使いがちです。
そうすると、Pythonでは構文エラーが出ることになります。

でも、一般的には構文エラーをあまり見る機会はないかもしれません。
なぜなら、コーディング中に判明するからです。

これは、PyCharmでコーディングしている最中の一コマです。
PyCharmについては、次の記事で説明しています。

別にPyCharmでなくても、IDEを使っていれば似たようなモノが表示されるはずです。
今どき、Pythonをメモ帳やviで開発している人はいないでしょう・・・

もしいたら、PyCharmをオススメしておきます。
日本語化にも対応しており、便利です。

例外 (exception)

文や式が構文的に正しくても、実行しようとしたときにエラーが発生するかもしれません。
そのような場合に起きるエラーは、基本的にはIDEで把握できません。
(NameErrorのように把握できる場合もあります)

そして、このようなエラーを例外と呼びます。
想定外というヤツです。

例えば、次のコードを実行した場合に例外が発生します。

10 * (1/0)

「ゼロ除算」です。
数値のゼロでは割れないというエラーになります。

    10 * (1/0)
ZeroDivisionError: division by zero

明確に「0」がコーディングされていれば、IDEでもエラーの把握はできるかもしれません。
しかし、割る数値が変数であれば、もうIDEでは把握できません。

つまりは、実行しないとわからないということです。
このように実行しないとわからないエラーを例外と呼びます。

以上、Pythonにおけるエラーについて説明しました。
次は、Pythonにおける例外処理について確認します。

Pythonにおける例外処理

以下は、Pythonにおける例外のクラス階層です。

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
      |    +-- ModuleNotFoundError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- EncodingWarning
           +-- ResourceWarning

これを見て、どう感じますか?
個人的には、「個別には対応をやってられないなー」という感じです。

そのため、以下のように済ませる場合が多いです。

try:
    # 例外が発生するかもしれない処理
    my_fnc()
except:
    print('Exception')

プログラムを途中で止めたくない場合は、これで十分でしょう。
ただ、エラー毎に処理を分岐させたいこともあるでしょう。

例えば、数値・計算での例外を細かく処理したい場合です。
そのような場合には、次のようにコーディングします。

try:
    1 / 0
except FloatingPointError as e:
    print('FloatingPointError')
except OverflowError as e:
    print('OverflowError')
except ZeroDivisionError as e:
    print('ZeroDivisionError')
except Exception as e:
    print('本当の想定外Exception')

ちなみに上記実行時の結果は、以下となります。

ZeroDivisionError

もちろん、例外処理が出来ているのでプログラムは途中で止まりません。
理想は、このように細かく処理することなのでしょう。

しかし、常に例外を想定してプログラミングすることは手間とも言えます。
コスト的に割に合わないケースも出てきます。

よって、そこは機能の重要度を判別することになります。

以上、Pythonにおける例外処理を説明しました。
次は、例外をスタックトレースで確認します。

例外をスタックトレースで確認

下記のままでは、例外の詳細はわかりません。

try:
    1 / 0
except:
    print('Exception')

このままでは、エラーを調査することはできません。
そこで、例外時の内容を把握するためにスタックトレースを利用します。

スタックトレースを確認するには、次のように記述します。

import traceback

try:
    1 / 0
except Exception as e:
    print(traceback.format_exc())

実行した結果は、次のように表示されます。

Traceback (most recent call last):
  File スクリプト名と行
    1 / 0
ZeroDivisionError: division by zero

これをログに出力するようにすれば、調査に利用できます。
スタックトレースで確認できた例外を考慮してコードを改良することも可能です。

import traceback

try:
    1 / 0
except ZeroDivisionError as e:
    print('ZeroDivisionError')
except Exception as e:
    print(traceback.format_exc())

こうやって日々改善していくやり方もあるでしょう。
だから、例外処理については「答え」はないと思います。

それぞれの環境において、例外処理の必要性や重要度は変わりますからね。

タイトルとURLをコピーしました