63:臆さずにエラーを発生させる

プログラミング迷子: 例外を発生させたくない

  • 先輩T:この def validate(data): 関数の中で data.get('ids') っていうコードがたくさんあるんだけど、フレームワークが data 辞書を用意して validate を呼んでくれるから、 'ids' は必ずあるんじゃない?

  • 後輩W:ありますね。

  • 先輩T:じゃあどうして data['ids'] じゃなく data.get('ids') なの?

  • 後輩W: 'ids' がない場合に例外を発生させないようにするためです。

  • 先輩T:???

  • 後輩W: validate に必ず 'ids' を持つ辞書を渡してくれるかわからないですよね。

  • 先輩T:それはフレームワークがよくわからないから過剰防衛してるだけでは。

例外を発生させるのは悪、と考えて、関数に渡される値のさまざまなケースに対応して過剰実装してしまうと、実際にはあり得ない引数のためにコードが複雑化してしまいます。 臆病になりすぎず、かつ問題の発生を見逃さないシンプルな方法があるでしょうか?

具体的な失敗

以下のコードは、関数に渡される辞書オブジェクトの中身を心配しすぎています。

def validate(data):
    """data['ids']を検査して、含まれる不正なidの一覧を返す
    """
    ids = data.get('ids')  # ここが問題
    err_ids = []
    for id in ids:
        if ...:  # idが不正かどうかをチェックする条件文
            err_ids.append(id)
    return err_ids

辞書オブジェクトが「キーを持っているかどうかわからない」から data.get('ids') というコードを書いたケースです。 この予防措置によって、 data.get('ids')None が返される可能性が生まれてしまっています。 もし None が返された場合、その2行後の for id in ids で結局エラーになってしまうため、この予防措置には意味がありません。 それどころか、 data.get('ids') と書いたために、 None が返された場合にどうすれば良いかを心配しながらその先のコードを書かなければいけなくなってしまっています。

cover

(中略)詳細は書籍 自走プログラマー をご参照ください

こういったコードは、例外が発生する可能性を気にしすぎて例外を隠蔽してしまったため、 バグに早く気づく ことができません。

ベストプラクティス

例外を隠すのではなく、わかりやすい例外を早く上げるコードを書きましょう。

辞書のキーがあってもなくても動作するコードを書くより、期待するデータが必ず渡される前提でコードを書くとシンプルになります。 もし呼び出し方を間違えた場合には、例外が発生するため問題に早く気づけます。

cover

(中略)詳細は書籍 自走プログラマー をご参照ください

また、引数のルールを自分で決められる場合は、 12:辞書でなくクラスを定義する も参照してください。