Pythonチュートリアル #6 -例外まみれ-

※以下の企画です

https://qiita.com/tatataichi/private/56936a028e625e62af8e

今回は8章をやっていきます。
8章は「エラーと例外」というタイトルで、普段何気なくおまじない的に使っているtry/exeptあたりをちゃんと学べそうな気がしています。
それではやっていきますぅ。

# 8章 学習内容まとめ
そもそもエラーには2種類あり、**構文エラー**と**例外**とのこと。
本章はそれらをそれぞれ学べる内容になっていた。
## 例外の処理
例外を待ち構える文法は「`try / except`」で書く。これはPythonに限らず様々な言語でサポートされている文法（`try / catch`の方が個人的には馴染みがあるが）
特に新しい学びはないかなーと思って読んでいたが、**`except`はタプルで複数種類待ち構えられる**というのは初めて知った。

```python: 複数種類のexcept
>> except(RuntimeError,TypeError,NameError):
>>   pass
```

また、`try / except`はオプションで`else`をつけられる。
これは`try`で例外が発生しなかった場合にジャンプする場所となっていて、`try`にコードをつらつら書くのではなく`else`句に書いていくことが推奨されていた。
`try / except`で本来プロテクトしなくて良い例外までキャッチしてしまうのを防ぐためらしい。これはたしかにそう。

```python: try except else
>> try:
>>   print('try')
>> except:
>>   print('except')
>> else:
>>   print('else')

try
else
```

## 例外の送出
例外は意図的に送出もできる。これは自主的に使ったことが無かったものの、ちゃんと使えれば質の高いコードになるなと感じた。
方法は簡単で`raise`を使うだけ。

```python: raise
>> raise NameError('oops')

---------------------------------------------------------------------------
NameError                                Traceback (most recent call last)
<ipython-input-2-41bcb0d57fce> in <cell line: 1>()
----> 1 raise NameError('oops')

NameError: oops
```

`except`と組み合わせももちろん可能。

```python: raise - except
>> try:
>>   raise NameError('oops!!!')
>> except NameError:
>>   print('例外だああああ！')
>>   raise


例外だああああ！
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-3-c755c7cd20cd> in <cell line: 1>()
      1 try:
----> 2   raise NameError('oops!!!')
      3 except NameError:
      4   print('例外だああああ！')
      5   raise

NameError: oops!!!
```
この場合、`except`句内で引数なしの`raise`を使用すると、**現在処理中の例外（この場合は NameError）を再度発生**させられる。
この仕様は本書だけだと読み取れなかったので、GPTに教えてもらった。
この処理を正しく使いこなせるかはわからないが、意識的に使えるよう脳に焼き付けておく…

## クリーンアップ動作
`try/except`構文には、`else`意外に実はまだもう一つの文法がある（他にもあるかもだが、紹介されていたのはこれが最後）。
それが`try / excepyt / finally`。
`finally`は`try`が例外を送出しようがしまいが関係なく実行される。
`else`は「**例外が発生しなかった場合に処理される**」ものであったので、この違いに注意したい。

```python: finally句
>> def divide(x,y):
>>     try:
>>         result = x / y
>>     except ZeroDivisionError:
>>         print("division by zero!")
>>     else:
>>         print("result is", result)
>>     finally:
>>         print("finally!")
```

上記のようなコードであれば、上手く割り算できようが、ゼロ除算が起きようが、他のエラー（文字列が入力されたとか）が起きようが、`finally`句は実行される。

 ```python: finally　実行結果
>> divide(1,2)

result is 0.5
finally!


>> divide(1,0)

division by zero!
finally!


>> divide("1","0")

finally!
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-5-598d3dd0ec63> in <cell line: 1>()
----> 1 divide("1","0")

<ipython-input-3-866838b2288c> in divide(x, y)
      1 def divide(x,y):
      2     try:
----> 3         result = x / y
      4     except ZeroDivisionError:
      5         print("division by zero!")

TypeError: unsupported operand type(s) for /: 'str' and 'str'
```

個人的には、大量のデータの書き出しとかに使えるんじゃないかなと思った。
ビッグデータの処理でCSVやらBigQueryやらに結果を格納していくときに、処理途中でエラー（想定外の無効な文字が入っていてとか）が生じた際に、`finally`句でそこまでの結果を書き出しておくみたいな。
エラー用に`except`句に書くよりもスマートに行けるんじゃないかなぁ。
これはぜひとも実務でも使ってみたい。

# まとめ
僕の書くコードはエラーまみれの例外まみれなので、この章は非常に勉強になった。
特に`finally`はなんで今まで調べなかったのだろうと後悔しつつ、新たな学びになって良かったとも思う。
初心に戻る大切さを今更ながら痛感した…。
次回も楽しくやっていきます〜