「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())
こうやって日々改善していくやり方もあるでしょう。
だから、例外処理については「答え」はないと思います。
それぞれの環境において、例外処理の必要性や重要度は変わりますからね。