Pythonのtry構文についてまとめてみた

<!-- Pythonのtry構文についてまとめてみた -->

※この記事は[Udon Advent Calendar 2024 - Adventar](https://adventar.org/calendars/10764)の23日目の記事です。

## はじめに

こんにちは。Udonです。このアドベントカレンダーも終盤ですね。

今日はPythonの`try`構文についてまとめてみたいと思います。最近Pythonを書いているときに、参考にしている記事でよくこの構文が使われていました。なので一回まとめ、しっかりと理解しておきたいと思います。

## `try`構文とは

プログラミングをしていると、エラーが発生することがあります。競技プログラミングみたいに短時間だけ動作するプログラムならいいですが、システムとして動作しているときにエラーが発生してしまうと、システムが停止して再起動を強いられてしまうことがあります。致命的な例外だったら改善の余地となるのですが、データの型を間違えたとか、ケアレスミスでエラーが発生することもあります。そういうエラーでシステムがいちいち停止していると、とても不便です。

そんな時に役に立つのが`try`構文です。`try`構文を使うと、エラーが発生してもプログラムが停止することなく、エラーをキャッチして、プログラムを停止させずに処理を続行することができます。

主に例外処理(プログラム言語が想定していない処理)が発生する可能性がある部分に使う構文です。

## 使い方

`try`構文は以下のように書きます。

```python
def calc(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        return "0で割ることはできません"
```

これは0除算を防ぐ機能を実装したものです。`b=0`の場合、通常はエラーが発生してプログラムが停止しますが、`try`構文を使うことでエラーをキャッチして、エラーメッセージを返すようになります。

また、Pythonは型が動的に変わるため、型エラーが発生することもあります。その場合も`try`構文を使ってエラーをキャッチすることができます。

```python
def calc(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        return "0で割ることはできません"
    except TypeError:
        return "型が違います"
```

これは`a`や`b`に数値ではなく文字列やリストなどが入っていた場合に発生するエラーをキャッチしています。`except`を追加することにより、複数のエラーをキャッチすることができます。

## `except`について詳しく

### エラーの種類

さっきから書いていますが、`except`の後にはエラーの種類を指定することができます。

まず、`ZeroDivisionError`は0除算をしたときに発生するエラーです。`TypeError`は型エラーが発生したときに発生するエラーです。他にも、`ValueError`や`IndexError`など、様々なエラーがあります。前者は値が不正な場合に発生するエラーで、後者はインデックスが不正な場合(範囲外参照など)に発生するエラーです。

また、`except`の後にエラーの種類を指定しない場合、全てのエラーをキャッチすることができます。

```python
def calc(a, b):
    try:
        return a / b
    except:
        return "エラーが発生しました"
```

これだと、0除算をしたときや型エラーを起こしたときに同じエラーメッセージを返すようになります。全ての例外をキャッチできますが、エラーの種類が分からないので、デバッグが難しくなります。

### `as`を使ってエラーの内容を取得する

`as`を使ってエラーの内容を取得することができます。

```python
def calc(a, b):
    try:
        return a / b
    except Exception as e:
        return e
```

こうすると、`e`にエラーの内容が格納されます。これを返せば、エラーメッセージを取得することができます。

```python
print(calc(10, 2)) #5.0
print(calc([], 2)) #unsupported operand type(s) for /: 'list' and 'int'
print(calc(10, 0)) #division by zero
```

エラーは英語ですが、しっかりと内容を把握することができます。逆に、英語が苦手な人には不向きかもしれません。そういう時は、エラーの内容を日本語に翻訳するライブラリを使ったり、各エラーに対して日本語のエラーメッセージを返すようにするといいかもしれません。

## その他指定できるもの

`try`構文には他にも指定できるものがあります。

### `else`

`else`を使うことで、エラーが発生しなかった場合に実行する処理を指定することができます。これを使うことでエラーが発生しなかった場合**のみに**実行したい処理を指定することができます。

```python
def calc(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        return "0で割ることはできません"
    else:
        return "計算が正常に終了しました"
```

### `finally`

`finally`を使うことで、エラーが発生してもしなくても実行する処理を指定することができます。

```python
def calc(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        return "0で割ることはできません"
    finally:
        print("計算が終了しました")
```

この場合、エラーが発生してもしなくても、`"計算が終了しました"`が表示されます。`try`内に書いても良い気がしますが、`try`内ではどうやらエラーが発生した場所から下の処理は実行されないようです。

```python
def calc(a, b):
    try:
        c= a / b
        print("計算終了")
        return c
    except Exception as e:
        return f"エラーが発生しました: {e}"

print(calc(10, 0)) #エラーが発生しました: division by zero
```

この場合、`計算終了`は表示されません。`finally`を使うことで、エラーが発生してもしなくても実行する処理を指定することができます。

```python
def calc(a, b):
    try:
        c= a / b
        return c
    except Exception as e:
        return f"エラーが発生しました: {e}"
    finally:
        print("計算終了")

print(calc(10, 0))

#エラーが発生しました: division by zero
#計算終了
```

## 応用事例

### ファイルの読み込み

ファイルの読み込み時にエラーが発生することがあります。その場合、`try`構文を使ってエラーをキャッチすることができます。

```python
try:
    with open("sample.txt", "r") as f:
        print(f.read())
except FileNotFoundError:
    print("ファイルが見つかりません")
```

ファイルを作り忘れた場合などには`FileNotFoundError`が発生します。これをキャッチしてエラーメッセージを表示します。

### ネットワーク通信

ネットワーク通信時にエラーが発生することがあります。その場合も`try`構文を使ってエラーをキャッチすることができます。

### メール送信

メール送信時にエラーが発生することがあります。その場合も`try`構文を使ってエラーをキャッチすることができます。

例えば、サーバに接続できなかったり(認証エラー)、メールアドレスが間違っていたり(メールアドレスエラー)、メールの内容が不正だったり(メール内容エラー)することがあります。これらのエラーをキャッチしてエラーメッセージを表示することができます。

## まとめ

以上のように、`try`構文を使うことでエラーをキャッチして、プログラムを停止させずに処理を続行することができます。

これによって、システムを停止させずともエラーの発生とその内容を把握し、対処を並行して進めることができます。結果、保守性や安定性が向上するわけです。

エラーが発生する可能性がある部分には、`try`構文を使ってエラーをキャッチすると良いと思います。

今回の記事は以上です。また明日の記事でお会いしましょう。

## 参考文献

[pythonのtry構文について調べてみた #Python - Qiita](https://qiita.com/keishi04hrikzira/items/6c1dc7cb5bc5ce2605f3)

[例外処理（try-except）を活用しよう #Python - Qiita](https://qiita.com/suipy/items/9d02e197df813f3c2fab)

[【Python】例外処理の使い分けについて：raise文、assert文、try-except文](https://zenn.dev/hovinci/articles/3d608096c0381)