============================= 63:臆さずにエラーを発生させる ============================= .. maigo:: 例外を発生させたくない * 先輩T:この ``def validate(data):`` 関数の中で ``data.get('ids')`` っていうコードがたくさんあるんだけど、フレームワークが ``data`` 辞書を用意して ``validate`` を呼んでくれるから、 ``'ids'`` は必ずあるんじゃない? * 後輩W:ありますね。 * 先輩T:じゃあどうして ``data['ids']`` じゃなく ``data.get('ids')`` なの? * 後輩W: ``'ids'`` がない場合に例外を発生させないようにするためです。 * 先輩T:??? * 後輩W: ``validate`` に必ず ``'ids'`` を持つ辞書を渡してくれるかわからないですよね。 * 先輩T:それはフレームワークがよくわからないから過剰防衛してるだけでは。 .. index:: 例外 例外を発生させるのは悪、と考えて、関数に渡される値のさまざまなケースに対応して過剰実装してしまうと、実際にはあり得ない引数のためにコードが複雑化してしまいます。 臆病になりすぎず、かつ問題の発生を見逃さないシンプルな方法があるでしょうか? 具体的な失敗 =============== 以下のコードは、関数に渡される辞書オブジェクトの中身を心配しすぎています。 .. code:: python 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`` が返された場合にどうすれば良いかを心配しながらその先のコードを書かなければいけなくなってしまっています。 .. omission:: こういったコードは、例外が発生する可能性を気にしすぎて例外を隠蔽してしまったため、 **バグに早く気づく** ことができません。 .. index:: バグに早く気づく ベストプラクティス ================== 例外を隠すのではなく、わかりやすい例外を早く上げるコードを書きましょう。 辞書のキーがあってもなくても動作するコードを書くより、期待するデータが必ず渡される前提でコードを書くとシンプルになります。 もし呼び出し方を間違えた場合には、例外が発生するため問題に早く気づけます。 .. omission:: また、引数のルールを自分で決められる場合は、 :doc:`../クラス設計/12-辞書でなくクラスを定義する` も参照してください。