現場でPythonを書くなら、例外処理が書けないとお話になりません。
例外処理が適切に実装されていないと、業務システムとしては不完全で使い物になりませんね。
本記事では、Pythonの例外処理について、初心者でも理解できるように基本から応用まで詳しく説明します。
Pythonを勉強する時、何から勉強するか分からず、挫折します。初心者でも、中級者でも、レベルに合わせた勉強方法を分かりやすくまとめています。
例外処理の書き方
例外処理とは、プログラムの実行中に起こりうる予期しないエラーに対処する方法のことです。
プログラムがエラーに遭遇すると、通常はエラーメッセージを出力して終了しますが、例外処理を使用すると、エラーが発生してもプログラムを続行できます。
例外処理の基本的な書き方は以下の通り
try:
# 例外が発生する可能性のある処理
result = function_that_may_raise_exception()
except SomeException as e:
# SomeExceptionが発生した場合の処理
print(f"An error occurred: {e}")
else:
# 例外が発生しなかった場合の処理
print(f"Result: {result}")
finally:
# 例外の有無に関わらず必ず実行する処理
print("Done")
例外処理のtry-except文の書き方と基本的な使い方
try
ブロックには、例外が発生する可能性のある処理を記述します。この処理中に例外が発生した場合、その例外が except
ブロックに渡されます。
except
ブロックには、発生した例外への対処内容を記述します。例外オブジェクトは、 as
キーワードを使用して変数に代入することができます。この変数には、発生した例外の情報が含まれます。
例外処理のelseブロックの使い方
else
ブロックには、 try
ブロック内の処理が例外を発生させずに、正常終了した場合に実行する処理を記述します。このブロックは省略可能です。
例外処理のfinallyブロックの使い方
finally
ブロックには、例外の有無に関わらず必ず実行する処理を記述します。このブロックは省略可能です。
例えば、以下のような感じです。
try:
# DB処理
result = db_exec()
except SomeException as e:
# 例外発生
print(f"An error occurred: {e}")
finally:
# 例外の有無に関わらず必ず実行する処理
session.close()
Pythonのraise文の使い方
raise文は、Pythonで例外を発生させるために使用されます。
raise文を使用すると、自分で例外を定義したり、組み込みの例外を発生させることができます。
以下は、raise文の使い方と例外の発生の例です。
def divide(a, b):
if b == 0:
raise ZeroDivisionError('ゼロ除算が発生しました')
return a / b
try:
result = divide(10, 0)
except ZeroDivisionError as e:
print(e)
上記のコードでは、divide関数内でゼロ除算が発生した場合に、raise文でZeroDivisionErrorを発生させます。exceptブロックでは、発生したZeroDivisionErrorをキャッチし、そのエラーメッセージを表示するように指定されています。
複数の例外の処理
Pythonでは、複数の異なる例外を処理することができます。
具体的には、以下のように実装します。
try:
# 例外が発生する可能性のあるコード
except (例外1, 例外2):
# 例外処理のコード
このように、except文の後に括弧で例外を複数指定することで、複数の例外を同じように処理できます。
発生した例外の内容に応じて、例外処理を変えたい場合は、以下の通りに実装します。
try:
# 例外が発生する可能性のあるコード
except ValueError:
# ValueErrorの例外処理
except TypeError:
# TypeErrorの例外処理
except:
# その他の例外の例外処理
複数のexcept文を書く場合、最も詳細な例外から順に例外処理を書きます。
以下のように、よくある例外の処理の順序を示します。
- 最も詳細な例外を処理するexcept文
- 継承関係にある例外を処理するexcept文
- 一般的な例外を処理するexcept文
例外の情報の取得
プログラムで例外が発生した場合、デバッグのために例外の情報を取得することができます。
Pythonには、例外の情報を取得するためのいくつかの方法があります。
例外の情報を取得する方法
Pythonのtry-except文で例外が発生した場合、例外オブジェクトが作成されます。この例外オブジェクトには、例外が発生したファイル名、行番号、エラーメッセージなどの情報が含まれています。
この例外オブジェクトを使って、例外の情報を取得することができます。
try:
# 例外が発生する可能性のある処理
except Exception as e:
# 例外オブジェクトを使って例外の情報を取得
print("例外が発生しました。")
print("エラーの種類:", type(e).__name__)
print("エラーメッセージ:", e)
この例では、except
ブロックで例外オブジェクトをe
として受け取っています。type(e).__name__
を使うことで、例外オブジェクトのクラス名を取得することができます。e
をそのままprint
すると、エラーメッセージが表示されます。
tracebackモジュールを使った例外情報の取得方法
Pythonのtracebackモジュールを使って、より詳細な例外の情報を取得することができます。
tracebackモジュールには、スタックトレースの情報を表示する関数が用意されています。スタックトレースには、例外が発生した場所から関数の呼び出し履歴が含まれています。
import traceback
try:
# 例外が発生する可能性のある処理
except Exception as e:
# tracebackモジュールを使って例外情報を取得
print("例外が発生しました。")
traceback.print_exc()
この例では、traceback.print_exc()
を使ってスタックトレースの情報を表示しています。traceback.print_exc()
は、現在の例外のスタックトレースを表示する関数です。
カスタム例外
Pythonでは、標準の例外だけでなく、自分で独自の例外を定義して使用できます。
独自の例外を作成することで、エラーメッセージをカスタマイズしたり、複数の例外をまとめてキャッチできます。
以下に、カスタム例外を作成して使用する方法を紹介します。
カスタム例外の定義
カスタム例外を定義するには、Pythonの組み込みの Exception
クラスを継承して新しいクラスを作成します。
class CustomError(Exception):
pass
上記の例では、 CustomError
という新しい例外クラスを作成しています。この例外クラスは、 Exception
クラスを継承しているため、 Exception
クラスの機能をすべて利用できます。
カスタム例外クラスには、任意の属性を定義することができます。
例えば、以下のように、エラーメッセージを持つ CustomError
クラスを定義することができます。
class CustomError(Exception):
def __init__(self, message):
self.message = message
上記の例では、 CustomError
クラスに __init__
メソッドを追加し、エラーメッセージを message
属性として保持するようにしています。これにより、例外が発生した場合にエラーメッセージをカスタマイズすることができます。
カスタム例外の使用
カスタム例外は以下のように利用できます。
def calculate(a, b):
if b == 0:
raise CustomError("division by zero")
return a / b
上記の例では、 calculate
関数で、ゼロ除算を検出した場合に CustomError
例外をraise
文で発生させます。
また、例外が発生すると、処理が即座に停止して、例外をハンドリングするまで、処理が進まなくなります。
カスタム例外を発生させた場合、通常の例外と同じように、try-exceptブロックでキャッチすることができます。
try:
result = calculate(10, 0)
except CustomError as e:
print(f"CustomError: {e.message}")
デバッグ時によく使われるツール
プログラムのデバッグを助けるツールは多数あります。
以下では、特にPythonにおいてよく使われるデバッグツールをいくつか紹介します。
print文
デバッグの基本中の基本です。コードの中に出力したい情報をprint文で出力することで、実行時の値の確認やプログラムの実行状態の確認ができます。
def add(a, b):
result = a + b
print(f"Addition of {a} and {b} is {result}") # デバッグ用の出力
return result
ただし、大規模なプログラムでは、print文で出力した情報が大量に出力されてしまい、情報を見失ってしまうことがあるため、適切に使用する必要があります。
ローカルで開発している時に、一時的にprint文を使ってデバックするという用途で使いましょう
assert文
assert文は、条件式が真でない場合に、AssertionErrorを発生させます。
デバッグ時に実行したい箇所にassert文を挿入し、その条件が成立していることを確認できます。
def divide(a, b):
assert b != 0, "Cannot divide by zero"
result = a / b
return result
pdb
Python標準のデバッグツールであるpdbを使用することで、コードの実行を一時停止し、ステップ実行や変数の値の確認、コードの途中での実行再開などができます。
以下は、pdbを使用して、コードの途中で一時停止して、変数の値を確認する例です。
import pdb
def divide(a, b):
pdb.set_trace() # ここで一時停止
assert b != 0, "Cannot divide by zero"
result = a / b
return result
print(divide(4, 2))
logging
loggingモジュールを使用することで、print文と同様にデバッグ情報を出力することができますが、出力するログレベルを指定することで、出力内容を柔軟にコントロールすることができます。
import logging
def divide(a, b):
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
logger.info(f"a: {a}, b: {b}")
try:
result = a / b
except ZeroDivisionError:
logger.error("division by zero")
else:
logger.info(f"Result: {result}")
また、ログの出力先をファイルにすることもできます。
Pythonで発生する代表的な例外クラス
以下にそれぞれの例外クラスに対応する発生する可能性のある例外状況の例を挙げます。
例外クラス | 概要 | 詳細 |
---|---|---|
NameError | 定義されていない変数や関数を参照した場合に発生する | 定義されていない変数を使用した場合 定義されていない関数を呼び出した場合 |
TypeError | 型が不適切な操作をした場合に発生する | 引数の型が不正である場合 演算子のオペランドの型が不正である場合 不正な型のオブジェクトを使用した場合 |
ValueError | 適切な型を持つが、不正な値が渡された場合に発生する | 関数に対して不正な引数が渡された場合 オブジェクトに対して不正な値が設定された場合 |
IndexError | シーケンスのインデックスが範囲外である場合に発生する | リスト、タプル、文字列などのシーケンスに対して、範囲外のインデックスを指定した場合 |
KeyError | 辞書に存在しないキーを指定した場合に発生する | 辞書に存在しないキーを指定した場合 |
ZeroDivisionError | 0で割り算をした場合に発生する | 0で割り算をした場合 |
FileNotFoundError | 指定されたファイルが存在しない場合に発生する | ファイルを開こうとしたが、指定されたファイルが存在しなかった場合 |
IOError | ファイルやストリームの入出力操作中にエラーが発生した場合に発生する | ファイルの読み書きに失敗した場合 |
ModuleNotFoundError | インポートしようとしたモジュールが見つからない場合に発生する | インポートしようとしたモジュールが見つからない場合 |
AttributeError | オブジェクトが指定された属性を持たない場合に発生する | 存在しない属性を参照した場合 存在しないメソッドを呼び出した場合 |
詳細や対処法は以下の記事で詳しく解説しています。
例外処理でよくある質問
以下は例外処理でよくある質問と回答です。
Python学習におすすめのコンテンツ
現役シリコンバレーエンジニアが教えるPython 3 入門 + 応用 +アメリカのシリコンバレー流コードスタイル
Udemyの講座でPythonなら、文句なしにおすすめ
- 分かりやすい言葉や具体的なコードで工夫して説明しているので、初心者でも理解できる。
- 初心者でも挫折しないように、丁寧に詳しく手順を説明している。
- 講座内で取り扱う課題は実践的なものが多く、書籍では身につかない応用力を身につけられる。
- 情報量が多いのに、講座の進み方が速いので、一回では理解が追いつかない。
- 課題の難易度が高すぎる場合がある。
- 講師のアクセントが特殊で、聞き取りにくいと感じたことがある。
テスト駆動Python 第2版
日本語で唯一、pytestだけに焦点を当てて、詳細に書かれた書籍です。
- 日本語でpytestの全てについて、解説されている唯一の書籍
- 段々とステップアップして、深くなるので、初心者でも、中級者でも、上級者でも、学びがある
- サンプルコードには、サンプルアプリが含まれているため、実践的に学べる工夫あり
- 章末にまとめがあり、復習の役に立つ
- 第7章に「どのようなテストを書くのか」についての解説があり、テストに対する考え方が深まる
- 後半になると、かなり高度な内容になるため、初心者には難しい
- タイトルに「テスト駆動開発」と書かれているが、該当部分の記述が少なく、テスト駆動開発を知りたい人は期待外れになるかも
間違えて、第1版を買わないように注意して下さい。
まとめ
Pythonの例外処理は、エラーが発生してもプログラムを停止させずに、適切な対応をするための重要な仕組みです。
例外処理は、try-except文を使うことで簡単に実装でき、例外クラスやraise文を使うことでカスタム例外を定義することもできます。
例外処理を使うことで、プログラムがより堅牢になり、予期せぬエラーによる停止を防ぐことができます。
コメント