sec03 - Pythonの例外処理入門:エラーの種類と安全なコードの書き方
スポンサーリンク

エラーとは何か

プログラムを実行しているときに予期せぬ問題が起きることがあります。これを「エラー」と呼びます。エラーが発生すると、その場でプログラムが停止することがあります。

Pythonには主に2種類のエラーがあります:

  • 文法エラー(SyntaxError):Pythonの文法に違反した場合に発生します。
  • 実行時エラー(RuntimeError/例外):プログラムの文法は正しいが、実行中に問題が発生した場合に発生します。

まずは基本の例として、ゼロで割ろうとした場合のエラーを見てみます。

x = 10
y = 0
z = x / y  # ZeroDivisionErrorが発生
ZeroDivisionError: division by zero

このコードでは、0で割ろうとしているためZeroDivisionErrorが発生し、プログラムは停止します。

よくあるエラーの種類

Pythonでよく出る例外の種類と発生する状況を理解しておくと、エラーの原因を素早く特定できます。

代表的な例:

  • ZeroDivisionError:0で割ろうとしたときに発生
  • ValueError:文字列を数値に変換しようとしたができなかった場合に発生
  • TypeError:型が適合しない操作を行った場合に発生

サンプルコード:

# ValueErrorの例
s = 'abc'
num = int(s)  # ValueErrorが発生

# TypeErrorの例
num = 10
text = '5'
result = num + text  # TypeErrorが発生
# ValueErrorの例
ValueError: invalid literal for int() with base 10: 'abc'

# TypeErrorの例
TypeError: unsupported operand type(s) for +: 'int' and 'str'

これらの例外が何を意味するかを理解することで、原因をすぐに特定できるようになります。

スポンサーリンク

try/exceptで例外を捕捉する

Pythonで例外を安全に扱う基本はtry/exceptです。tryブロック内でエラーが発生した場合に、exceptブロックで処理を行います。

try:
    x = 10 / 0
except ZeroDivisionError:
    print('ZeroDivisionErrorを捕捉: 0で割ろうとしました')

出力例:

ZeroDivisionErrorを捕捉: 0で割ろうとしました

補足説明:try内で発生したZeroDivisionErrorexceptで捕捉し、プログラムを停止させずにエラーメッセージを表示しています。

as e: の使い方

exceptの後にas eを使うと、発生した例外オブジェクトを変数に代入して詳細情報を取得できます。

try:
    x = 10 / 0
except ZeroDivisionError as e:
    print('エラー内容:', e)

出力例:

エラー内容: division by zero

補足説明:as eを使うことで、エラーの種類だけでなくエラーメッセージなどの詳細情報も扱えるようになります。ログ記録やデバッグ時に非常に便利です。

複数の例外を捕捉する

1つのtryブロック内で、複数の例外を個別に捕捉することができます。

s = 'abc'
try:
    num = int(s)
    result = 10 / num
except ValueError:
    print('ValueErrorを捕捉: 文字列を数値に変換できません')
except ZeroDivisionError:
    print('ZeroDivisionErrorを捕捉: 0で割ろうとしました')

出力例:

ValueErrorを捕捉: 文字列を数値に変換できません

補足説明:各例外に対して別々のexceptブロックを用意することで、エラー原因ごとに適切な処理を行えます。

elseとfinallyの使い方

try/exceptにはelsefinallyを追加できます。

  • else:例外が発生しなかった場合に実行されるブロック
  • finally:例外の有無に関わらず必ず実行されるブロック
try:
    num = int('10')
except ValueError:
    print('ValueErrorが発生')
else:
    print('数値変換成功:', num)
finally:
    print('処理終了')

出力例:

数値変換成功: 10
処理終了

補足説明:elseは成功時に処理をまとめられるため、可読性が上がります。finallyは必ず実行されるので、ファイルやネットワークの後始末に便利です。

関数内での例外処理

関数内でも例外処理は可能です。例外を捕捉して安全に処理したり、必要に応じて呼び出し元に例外を伝えたりできます。

def safe_divide(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        print('ZeroDivisionErrorを捕捉: 0で割ろうとしました')
        return None

result = safe_divide(10, 0)
print(result)

出力例:

ZeroDivisionErrorを捕捉: 0で割ろうとしました
None

補足説明:関数内で例外を処理することで、呼び出し元が安全に関数を使えるようになります。

ループ内での例外処理

複数データを処理する場合、ループ内で個別に例外処理を行うと、1つのデータでエラーが発生しても残りのデータ処理に影響しません。

numbers = ['10', '5', 'abc', '0', '8']
total = 0

for s in numbers:
    print(f'number {s}:', end='\t')
    try:
        num = int(s)
        total += 10 / num
    except ValueError:
        print(f'ValueError: "{s}"は数字に変換できません')
    except ZeroDivisionError:
        print('ZeroDivisionError: 0で割ろうとしました')
    else:
        print(f'正常処理 [total = {total}]')

print('合計:', total)

出力例:

number 10:      正常処理 [total = 1.0]
number 5:       正常処理 [total = 3.0]
number abc:     ValueError: "abc"は数字に変換できません
number 0:       ZeroDivisionError: 0で割ろうとしました
number 8:       正常処理 [total = 4.25]
合計: 4.25

補足説明:ループ内で例外処理を行うことで、一部のデータでエラーが出ても処理全体が止まりません。

応用演習で例外処理を組み込む

複数ファイルから数字を読み込み、合計を計算する応用例です。ファイルが存在しない場合や数字以外のデータが入っている場合も安全に処理します。

Visual Studio Codeで実行する場合、Pythonファイルと同じディレクトリに、'numbers1.txt', 'numbers2.txt', 'numbers3.txt'を作成し、実行結果を確認してみてください。

file_list = ['numbers1.txt', 'numbers2.txt', 'numbers3.txt']
total = 0

for filename in file_list:
    try:
        with open(filename, 'r') as file:
            data = file.read()
        number = int(data)
    except FileNotFoundError:
        print(f'{filename} が見つかりません')
        continue
    except ValueError:
        print(f'{filename} のデータ("{data}")が数字ではありません')
        continue
    else:
        print(f'{filename} の読み込み成功: {number}')
        total += number

print('合計:', total)

出力例:

numbers1.txt の読み込み成功: 150
numbers2.txt が見つかりません
numbers3.txt のデータ("abc")が数字ではありません
合計: 150

補足説明:応用演習では、複数の例外を組み合わせて処理しています。elseを使うことで成功時の処理をまとめられ、continueで次のデータに進むことができます。

例外クラスの階層

Pythonの例外は階層構造になっています。すべての例外はBaseExceptionから派生しており、通常私たちが扱うほとんどの例外はExceptionクラスの下にあります。

各エラーの詳細はPython: 組み込み例外を参照してください。

BaseException
 ├── BaseExceptionGroup
 ├── GeneratorExit
 ├── KeyboardInterrupt
 ├── SystemExit
 └── Exception
      ├── ArithmeticError
      │    ├── FloatingPointError
      │    ├── OverflowError
      │    └── ZeroDivisionError
      ├── AssertionError
      ├── AttributeError
      ├── BufferError
      ├── EOFError
      ├── ExceptionGroup [BaseExceptionGroup]
      ├── 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
      │    ├── PythonFinalizationError
      │    └── RecursionError
      ├── StopAsyncIteration
      ├── StopIteration
      ├── SyntaxError
      │    └── IndentationError
      │         └── TabError
      ├── SystemError
      ├── TypeError
      ├── ValueError
      │    └── UnicodeError
      │         ├── UnicodeDecodeError
      │         ├── UnicodeEncodeError
      │         └── UnicodeTranslateError
      └── Warning
           ├── BytesWarning
           ├── DeprecationWarning
           ├── EncodingWarning
           ├── FutureWarning
           ├── ImportWarning
           ├── PendingDeprecationWarning
           ├── ResourceWarning
           ├── RuntimeWarning
           ├── SyntaxWarning
           ├── UnicodeWarning
           └── UserWarning

例:ArithmeticErrorZeroDivisionErrorOverflowErrorFloatingPointErrorの親クラスです。

この階層を理解すると、複数の例外をまとめて捕捉したり、特定の例外だけを捕捉したりできます。

次のコードのように、親クラスのArithmeticErrorを使うと、算術に関するエラーをまとめて捕捉できます。

try:
    x = 10 / 0
except ArithmeticError:
    print('算術エラーが発生しました')  # ZeroDivisionErrorも捕捉される

次のコードのように、ZeroDivisionErrorのみ捕捉するようなコードを書くと、エラーの範囲を絞ることができます。

try:
    x = 10 / 0
except ZeroDivisionError:
    print('0で割ろうとしました')  # OverflowErrorやFloatingPointErrorは捕捉されない
スポンサーリンク