Python
ポエム
例外処理
zaif

仮想通貨の自動監視プログラムで学んだ例外処理の良さ

Javaを学んでた時は謎だった例外処理を,今回少し理解できた気がする.
「わざわざ面倒な例外処理するメリットが謎」という学習者がもしいたら,例外処理が活きる一例として役に立てば幸い.

仮想通貨レートの自動監視

このプログラムを作る過程で,例外処理がわかってきた.
言語:Python3
概要:Zaifの仮想通貨レートを自動監視
ライブラリ:GitHub - techbureau/zaifapi (ありがとうございます)

やったことはZaifの公式サイトを眺めてるのと同じ.ひたすらapi経由でZaifに接続し,通貨レート情報を持ってくるだけ.

# -*- coding: utf-8 -*-
from zaifapi import ZaifPublicApi

zaif = ZaifPublicApi()
# 繰り返しは10回
for i in range(10):
    while (1):
        err = False

        try:
            askBJ = zaif.depth('btc_jpy')['asks'][0][0]
            askMJ = zaif.depth('mona_jpy')['asks'][0][0]
            askMB = zaif.depth('mona_btc')['asks'][0][0]
            bidBJ = zaif.depth('btc_jpy')['bids'][0][0]
            bidMJ = zaif.depth('mona_jpy')['bids'][0][0]
            bidMB = zaif.depth('mona_btc')['bids'][0][0]

        except:
            err = True
            print('except')

        if (err == False):
            break

    print(askBJ, end=', ')
    print(askMJ, end=', ')
    print(askMB, end=', ')
    print(bidBJ, end=', ')
    print(bidMJ, end=', ')
    print(bidMB)

上記プログラムのtry内を処理してる時に接続エラーが起こると,例外発生を検知してexcept内の処理が行われ,ループから抜けられずtryを繰り返す.
つまり「接続ミスしたらもう一度接続」を,接続成功するまで繰り返す.

僕の感覚では,例外処理の良さはこの3つだと思う.
1. 例外が起きても落ちない
2. エラーの原因がわかりやすい
3. エラーをまとめておける

1. 例外が起きても落ちない

偏った見方だが,例外が起こる部分をtryに囲むと,exceptでpass(何もしない)しても落ちなくなる.
無視した例外が波及して別の場所でエラーを起こす・・・なんてことになると結局落ちるが,上記プログラムなら接続エラーくらいしか起こらないので踏ん張ってくれる.

昔の僕はそういうこともわからなかったので,例外のことを「執拗にプログラムを落とそうとする謎の悪魔」だと本気で思ってた.

【2018/2/5追記】
ご同類がいた模様

SIerの下請け開発者ってレベル低すぎない?
フレームワークやライブラリのやっていることを理解しない
~中略~
こういうひとたちは典型として「例外」を全く理解できません。なんのためにあるのかわからず、自分のコーディング作業を邪魔するもの程度の認識しかありません。

try {
...
}catch(Exception e) {
return false;
}

こういうコードを書いてせっかくのエラー情報を握りつぶしたりするのです。

僕の例も確かにこれで,対策も「なかったことにする」だから耳がもげる.

言い訳をすると,僕の場合は「接続しなおせばOK」なのでまだマシだと思う.

要は「黙って再接続してほしいのに逐一落ちるな」という意味でのexceptスルー.

もちろん例外内容を取っとかないと,「実はAPIキーが違う」とか「そんな通貨ペアはない」みたいな例外が出ても発見できないので長期的にはアウト.

2. エラーの原因がわかりやすい

※エラーの原因特定に例外処理が役立つ,とダイレクトな記事があった.

運用者が困る例外処理の書き方
運用者はシステムでエラーが発生した際に、プログラムから出力されたログを見て原因の特定作業を行います。このとき重要となるのが例外処理です。適切な例外処理が行われていれば、原因の特定が容易になります。

本稿プログラムでも,完成前はException: return status code is 504というエラーにしばらく悩まされたが,
実はこの時点で問題が504エラーであることを教えてもらっていたわけで,最終的には「通るまで接続試行をやめなければおk」にたどり着けた.

これがもし例外じゃないエラーで「変数の型が違う」とかだったら,
なんで型が違うんだデバッグ→何も問題は無いのに何故だorz→しばらく回してると,時々同じエラーが起こるな→通信の部分で受信データが狂ってるぞ,これで型が違ったのか

と来てようやく原因がわかり,さらに
なんで正常な時と狂ってる時がある?→定期的に起こってるような→自分側? 相手側?→相手側のサーバが調子悪くなってんのか!

ここで初めて504エラーに気付く,ような気がする(例が悪いかもしれない).

そういう過程をすっ飛ばして即座に「504エラー出たんで」と教えてくれるのが例外.

3. エラーをまとめておける

※例外が起きてもプログラムが落ちない,と書いたが,プログラムが落ちる原因は例外自体ではなく,値がおかしいとか接続ミスとかのはず.
例外はむしろ原因を教えてくれる仕組みだが,初学者の段階でドカドカ見せつけられた結果勘違いしてしまったのかもしれない.

例外処理を利用すると,細かい部分がどうだろうと「根本が同じ問題」なら同じ例外として扱える.
int型にstr型を渡そうとしてもその逆をやろうとしても,変数の型が違うという問題は同じなのだから,if文を2つ書かなくても例外としてTypeErrorにまとめられる(例が悪いかもしれない).

これを延長しまくれば「何か問題があれば例外が起こる.例外が起こったら例外処理で全部スルーだ!」みたいなゴリ押しも書けないことはない.

今回利用したライブラリにも,変な値を検出したらわざと例外扱いにするような節が見られる(504エラーがそうじゃねとか思ってる).利用側は細かいことを気にしなくて良い,という効率的な仕組みに見えてちょっと素敵.