JavaおじさんがPythonを使えるようになるまでの全記録（その10）

#例外とアサーション
[テキスト](https://www.amazon.co.jp/gp/product/4764905183/ref=oh_aui_detailpage_o02_s00?ie=UTF8&psc=1)も基本文法の解説はあと残り2章。
残り16章分がアルゴリズムやら統計やら機械学習やらに充てられているという実に濃いテキストである。

そんなわけで7章の「例外とアサーション」を読んでいる。

例外を捕捉するためにはJavaでtry～catchを使ったのと同じ感覚でtry～expectを使ってやればOK、ということね。
このテキストの書きっぷりだとfinallyにあたる共通的な後処理の記述はできなさそうだけど、このテキスト後出し結構あるからな...ファイルのクローズとかする場合finally必須なのでたぶんあるんだろうな。
...って思ってたらVisual Studio CodeのIntellisenseにちゃんと候補としてfinally出てきたわ。
さすが賢いな。

とりあえず指練習でも。

> 以下のsumDigitsをtry-exceptを使って書け。

```7-1.py
def sumDigits(s):
    """sを文字列とする。
       文字列内に含まれる数値を合計した値を返す。"""
    sum = 0
    for c in s:
        try:
            sum += int(c)
        except ValueError:
            continue
    return sum

print('sumDigits(\'123\') :', sumDigits('123'))
print('sumDigits(\'a2b3c\') :', sumDigits('a2b3c'))
print('sumDigits(\'abc\') :', sumDigits('abc'))
```

実行するとこうなる。

```
sumDigits('123') : 6
sumDigits('a2b3c') : 5
sumDigits('abc') : 0
```

実際にはこれって例外を握りつぶしていることになるので、本来的にはよくないコーディングではある。
けどまあ今回は練習なのでね。

今回は``except ValueError``で特定の例外を処理したけど、こう書くとどんな例外でも捕捉はできる。

```7-1-all.py
def sumDigits(s):
    """sを文字列とする。
       文字列内に含まれる数値を合計した値を返す。"""
    sum = 0
    for c in s:
        try:
            sum += int(c)
        except:
            continue
    return sum

print('sumDigits(\'123\') :', sumDigits('123'))
print('sumDigits(\'a2b3c\') :', sumDigits('a2b3c'))
print('sumDigits(\'abc\') :', sumDigits('abc'))
```

でもってタプルで例外を列挙することで複数例外をまとめて1つのブロックで捕捉できる。

```7-1-tuple.py
def sumDigits(s):
    """sを文字列とする。
       文字列内に含まれる数値を合計した値を返す。"""
    sum = 0
    for c in s:
        try:
            sum += int(c)
        except (ValueError, TypeError):
            continue
    return sum

print('sumDigits(\'123\') :', sumDigits('123'))
print('sumDigits(\'a2b3c\') :', sumDigits('a2b3c'))
print('sumDigits(\'abc\') :', sumDigits('abc'))
```

ケースバイケースではあろうけれども例外はできる限り詳細に捕捉する方が好みではあるのよね。
なのでたぶん最初の書き方が一番推奨されるような気はする。

#フロー制御機構としての例外
と言ってるけど結局自分で例外を発生させるときの書き方の話。
例外処理に関してはほぼJavaと同じ感覚で使っていいのかしらねー。

- 例外はビルトイン例外の他にExceptionを継承して独自例外を作成することも可能。
- ``raise 例外名(引数)``で例外を発生させられる。

で、指練習

> findAnEvenを実装せよ。

```7-2.py
def findAnEven(L):
    """Lをint型の要素を持つリストとする。
       Lに最初に現れる偶数を返す。
       Lが偶数を含まなければValueErrorを引き起こす。"""
    evenList = [x for x in L if x%2 ==0]
    if len(evenList) == 0:
        raise ValueError('Lに奇数しか含まれていません。')
    else:
        return evenList[0]

try:
    source = [1, 2, 3, 4, 5]
    print(source)
    print('findAnEven(source) :', findAnEven(source))

    source = [1, 3, 5]
    print(source)
    print('findAnEven(source) :', findAnEven(source))
except ValueError as msg:
    print('Exception raised. msg:', msg)

try:
    source = [1, '3', 5]
    print(source)
    print('findAnEven(source) :', findAnEven(source))
except TypeError as msg:
    print('Exception raised. msg:', msg)
```

これを実行すると以下のようになる。

```
[1, 2, 3, 4, 5]
findAnEven(source) : 2
[1, 3, 5]
Exception raised. msg: Lに奇数しか含まれていません。
[1, '3', 5]
Exception raised. msg: not all arguments converted during string formatting
```
うん、おなじみの機構ですね。

#アサーション
アサーションも書けます、っと。

``assert 論理式(, 引数)```

引数は例外が引き起こされたとき（＝論理式がFalseになった場合にAssertionErrorが送出される）のメッセージになる、そうな。

結局のところここら辺の例外周りの機構はDesign by Contractの文脈で実装されている機能なのでさほど大きく変わることはない、というかむしろ違っている方がおかしいという感じではあるかな。

今日は連日の暑さにやられてバテバテなのでこれでおしまい。