早期リターンでネストを浅く! 読みづらいif文をスッキリさせるリファクタリング手法
Pythonをブラウザで実行しながら実践的に学ぶ
Pythonの基礎からソフトウェアアーキテクチャ,アルゴリズムなどの応用的な内容まで幅広く学べます。
ブラウザ上で直接Pythonコードを試すことができ、実践的なスキルを身につけることが可能です。
あなたが書いたコードを数日後に見返したとき、右へ右へとインデントが深まっていき、まるで階段のような形になっていたことはありませんか?
それはプログラミングの世界でネスト(入れ子)と呼ばれるもので、コードの読みやすさを損なう最大の原因の一つです。
特に初心者のうちは、条件を一つずつ慎重に重ねていくため、どうしてもネストが深くなりがちです。 しかし、そのまま放置すると、どこでどの条件が閉じているのか分からなくなり、バグの温床になってしまいます。
今回は、そんな複雑なif文を魔法のようにスッキリさせる早期リターンというテクニックをご紹介します。 エンジニア歴10年の私が、現場で何度も救われてきたこの手法をマスターして、脱・初心者の第一歩を踏み出しましょう。
なぜあなたのコードは読みづらいのか?「ネスト」という迷宮¶
プログラムを書いていると、エラーをチェックしたり、特定の条件だけで処理を行いたい場面がたくさん出てきます。 そのたびにif文を重ねていくと、本来やりたかったメインの処理が、深いインデントの奥底に埋もれてしまうのです。
この状態は、物語の核心にたどり着くまでに何度も横道に逸れなければならない小説を読んでいるようなものです。 読み手は、今自分がどの条件の中にいるのかを常に記憶しておかなければならず、脳に大きな負担がかかります。
階段のようなコードが脳に与えるストレス¶
ネストが深くなると、エンジニアは認知負荷という壁にぶつかります。 if文の中にさらにif文があり、その中にまたif文があるという構造は、人間の短期記憶の限界を超えてしまうからです。
例えば、5階層のネストがある場合、一番奥の処理を読むときには、それまでの4つの条件すべてを頭の中で保持していなければなりません。 これは非常に疲れやすく、読み間違いやロジックの見落としを誘発する、まさに危険な階段と言えます。
関連記事綺麗なコードって何?初心者から一歩抜け出す「リーダブルコード」の3つの基本
elseの呪縛が思考を止める¶
ネストが深くなる原因の多くは、if文に対して律儀にelse文を書きすぎてしまうことにあります。 もし〇〇ならAをする、そうでなければBをする、という二者択一を繰り返すと、コードの階層はどんどん増えていきます。
しかし、実際のプログラムでは、例外的なケースを先に片付けてしまったほうが、メインロジックが際立つことが多いのです。 elseを使わずに済む方法を知るだけで、あなたのコードの視認性は劇的に向上します。
早期リターンという救世主¶
早期リターンとは、関数の途中で条件に合わないことが判明した時点で、すぐに結果を返して処理を終了させる手法です。 これにより、本来の目的であるメインの処理を、インデントの一番浅い場所に配置することが可能になります。
この考え方は、入り口でチケットを確認し、持っていない人をその場で帰す受付窓口に似ています。 一度建物の中に入れてから、あちこちの部屋を回らせた後に、やっぱりチケットがないのでダメですと言う必要はないのです。
ガード節(Guard Clause)の考え方¶
早期リターンで使われる条件分岐のことは、プログラムを守るという意味でガード節と呼ばれます。 不正なデータやエラーになるケースを関数の冒頭で弾いてしまうことで、その後の処理が安全であることを保証する役割です。
ガード節を使いこなせるようになると、関数の構造が非常にシンプルになります。 例外を先に処理して視界から消し去ることで、私たちはメインのロジックを考えることに集中できるようになるのです。
【関連記事】Pythonの例外処理のアンチパターン5選をご紹介!|初心者がやりがちな「べからず集」
メインロジックを主役に据える¶
早期リターンを導入すると、関数の最後の方に書かれていたメインの処理が、どんどん上の階層に移動してきます。 コードをパッと見ただけで、この関数が最終的に何をしたいのかがすぐに伝わるようになります。
プログラミングは書く時間よりも読まれる時間のほうが圧倒的に長いと言われています。 未来の自分やチームメイトのために、メインロジックを主役として目立たせる工夫は、プロとして不可欠なスキルです。
実践!リファクタリング・ビフォーアフター¶
理屈だけではイメージが湧きにくいかもしれませんので、具体的なコードを見ていきましょう。 ユーザーが会員サイトにログインし、特別なコンテンツを表示できるかどうかを判定する処理を想定してみます。
まずは、初心者がやりがちな、ネストが深くなってしまったビフォーのコードです。 条件が増えるたびに右側にズレていく様子に注目してください。
# ネストが深くて読みづらいコード(ビフォー)
def show_premium_content(user):
if user is not None:
if user.is_logged_in:
if user.has_subscription:
if not user.is_account_suspended:
# ここがメインの処理。深い場所に埋もれている
content = "特別なコンテンツを表示します"
return content
else:
return "アカウントが停止されています"
else:
return "サブスクリプションが必要です"
else:
return "ログインしてください"
else:
return "ユーザー情報が見つかりません"
いかがでしょうか。一番大事な「コンテンツを表示する」という一行が、4つものif文の影に隠れてしまっています。 また、else文がずっと下の方にあるため、どのif文に対応しているのかを探すのも一苦労です。
では、これを早期リターンを使って書き換えてみましょう。
# 早期リターンでスッキリさせたコード(アフター)
def show_premium_content(user):
# ガード節:例外的なケースを先に弾く
if user is None:
return "ユーザー情報が見つかりません"
if not user.is_logged_in:
return "ログインしてください"
if not user.has_subscription:
return "サブスクリプションが必要です"
if user.is_account_suspended:
return "アカウントが停止されています"
# メインの処理が一番浅い階層に!
return "特別なコンテンツを表示します"
見違えるほど読みやすくなったのが分かりますか。 このコードなら、上から順番に読んでいくだけで、条件に合わない人が排除されていく流れがスムーズに理解できます。
エンジニア歴10年の私が痛感した「読みやすさ」の正体¶
私はこれまで、数万行規模のシステム開発や、多くの新人エンジニアのコードレビューを行ってきました。 その中で確信したのは、優れたコードとは、読んでいる途中で一度も立ち止まらせないコードだということです。
早期リターンを徹底しているコードは、まるで川が上から下へと流れるように、淀みなく内容が入ってきます。 逆にネストが深いコードは、あちこちで視線が上下左右に飛び、まるで迷路を解いているような疲労感を与えます。
コードレビューでネストが指摘される理由¶
コードレビューの場で、シニアエンジニアがよくネストの深さを指摘するのは、単なる見た目の好みの問題ではありません。 ネストが深いということは、それだけテストの漏れが発生しやすいというリスクを含んでいるからです。
分岐が複雑になればなるほど、すべてのパターンを網羅するのが難しくなり、想定外のバグを見逃しやすくなります。 早期リターンを推奨するのは、システムの品質を保ち、将来のメンテナンスコストを下げるための合理的な判断なのです。
思考のショートカットを作る¶
ベテランエンジニアは、コードを全て精読するのではなく、重要なポイントをスキャンするように読みます。 早期リターンが適切に使われていれば、不要なケースを脳内で即座に切り捨て、重要な箇所だけを重点的にチェックできます。
このように、読み手に思考のショートカットを提供できるエンジニアは、チーム内で非常に高く評価されます。 技術力とは、難しいことを書く力ではなく、難しいことをいかに簡単に伝えるかという力なのだと私は考えています。
早期リターンを使いこなすための比較表¶
ここで、従来のネスト型と早期リターン型の違いを分かりやすく整理してみましょう。 どちらの手法をいつ使うべきか、判断の基準として活用してください。
| 特徴 | 従来のネスト型 | 早期リターン型 |
|---|---|---|
| 可読性 | 右側に伸び、全体像が掴みにくい | 上から下へ流れ、要点が明確 |
| elseの数 | ifの数だけ増えやすく、対応が複雑 | ほとんど不要になり、シンプルになる |
| メイン処理 | インデントの深い部分に位置する | 関数の終盤、浅い部分に堂々と位置する |
| バグのリスク | 条件の閉じ忘れや矛盾が起きやすい | 漏れが少なく、デバッグが容易 |
| 推奨シーン | 非常に単純な2分岐のみ | 条件が3つ以上重なる複雑な処理 |
基本的には、条件が重なる場面では常に早期リターンを第一候補として考えるのが現代的なコーディングスタイルです。 もちろん、無理に全てを1行にまとめる必要はありませんが、ネストを3階層以上にしないというルールを持つだけでも世界が変わります。
早期リターンをさらに進化させるテクニック¶
Python 3.10以降を使っているなら、さらに強力な武器があります。 それがパターンマッチング(match-case文)です。
if文が何重にも重なるような複雑な条件分岐は、このmatch-caseを使うことで、早期リターン以上に美しく書けることがあります。 早期リターンとパターンマッチング、この二つを状況に応じて使い分けられるようになれば、あなたのコードは見やすくなるはずです。
詳細はこちらをご覧ください。 Python 3.10で追加された「パターンマッチング(match-case)」入門!if-elif地獄から脱出!
ロジックを小さな関数に切り出す¶
早期リターンを使っても、なお条件分岐が長くなってしまうことがあります。 そんな時は、その条件チェック自体を別の小さな関数として切り出すリファクタリングを検討してください。
is_valid_user(user) という名前の関数を作り、その中で早期リターンを行ってTrue/Falseを返せば、元の関数はさらにスッキリします。
一つの関数に詰め込みすぎず、役割を細かく分けていくことも、ネストを浅く保つための重要なコツです。
三項演算子との使い分け¶
非常にシンプルな条件であれば、Pythonの三項演算子を使うのも手です。
value = A if condition else B という書き方ですね。
ただし、これを複雑なロジックで使うと、逆に1行が長くなりすぎて読みづらくなります。 基本は早期リターンを使い、本当に行数が短い場合だけ三項演算子を使うといった、バランス感覚を養いましょう。
早期リターンを導入する際の注意点¶
これほど便利な早期リターンですが、使い所を間違えると逆に混乱を招くこともあります。 何事もやりすぎは禁物であり、コードの目的を常に忘れないことが大切です。
ここでは、私が過去に失敗した経験も踏まえ、注意すべきポイントをいくつかお伝えします。
出口(return)が多すぎる問題¶
一つの関数の中にreturnが10箇所も20箇所もあると、今度はどこで処理が終わったのかを追うのが難しくなります。 関数の規模が大きくなりすぎているサインですので、まずは関数を分割することを考えてください。
理想は、関数の冒頭に数個のガード節があり、最後にメインの結果を返すという構成です。 出口が多すぎて迷路になっていないか、常に客観的な視点で自分のコードを見つめ直しましょう。
終了処理の忘れに注意¶
ファイルを閉じたり、データベースの接続を解除したりといった、共通の終了処理が必要な場合は注意が必要です。 途中でreturnしてしまうと、その後のクリーンアップ処理が実行されない可能性があります。
このような場合は、Pythonのwith構文を使ったり、try-finally文を活用して、どこでreturnしても確実に終了処理が行われるように工夫しましょう。
リソース管理と早期リターンの相性を理解しておくことも、プロエンジニアへの道です。
まとめ:今日から「浅いネスト」を意識しよう¶
早期リターンは、今日からすぐにでも始められる、非常にコストパフォーマンスの高い改善手法です。 最初は、elseを書かないと不安と感じるかもしれませんが、一度そのスッキリ感を知ると、もう元の書き方には戻れなくなります。
コードは書くものではなく、誰かに伝えるための手紙です。 ネストという高い壁を取り払い、メインのメッセージを一番目立つ場所に置いてあげましょう。
これまで書いてきた自分のコードを、一つだけでいいので早期リターンで書き換えてみてください。 その瞬間の「あ、読みやすくなった!」という感動こそが、あなたのエンジニアとしての感性を研ぎ澄ませてくれます。
もし、もっと深くリファクタリングについて学びたくなったら、他の記事も参考にしてみてくださいね。 あなたのコードが、より美しく、誰にでも優しいものになることを応援しています。