Pythonでは、関数内に関数を定義する「ネスト構造」が柔軟に使える。一時的なユーティリティ関数やクロージャの構築など、文法上の強みを活かした設計が可能になる一方で、過剰なネストは可読性と保守性の低下を招く原因となる。
レビューアーはこのネスト構造に対し、単に「見た目が深いから悪い」と判断するのではなく、構造の目的・責務の明確さ・再利用性・テスト性など、多角的な観点から評価することが求められる。

Pythonにおけるネスト関数の例とその問題点

まず典型的なネスト関数の例を見てみよう。

ネスト構造の例
def handle_request(request):
    def authenticate(token):
        return token == "valid"

    def transform(data):
        return data.strip().lower()

    if not authenticate(request.token):
        return "unauthorized"

    return transform(request.payload)

この程度のネストは関数のスコープを限定する意図が明確で、構造も単純であるため許容されることが多い。

しかし、処理が増えるとネストが階層的に深くなり、構造が次第に把握しづらくなる。

ネスト過多の例
def process():
    def stage1(data):
        def normalize(x):
            def clamp(v):
                return max(0, min(100, v))
            return [clamp(n) for n in x]
        return normalize(data)

    def stage2(data):
        def encode(x):
            return [str(n) for n in x]
        return encode(data)

    data = [120, -5, 75]
    return stage2(stage1(data))
過剰ネストの典型症状
  • 関数の内側に次の関数が埋まる構造となり、スコープの境界が不明瞭になる
  • テストが困難(ネスト内の関数はモック化や直接呼び出しがしにくい)
  • 再利用不可(他の処理からアクセスできない)

レビュー観点1:関数の深さと認知負荷

レビューアーがネスト構造を見る際、まず意識すべきは「人間が追える構造かどうか」である。一般的には3段以上のネスト構造は読解負荷が急激に高まるとされている。

UML Diagram

このように「階層の深さ」を最初にチェックし、深ければ責務分割や関数抽出の提案をするのが基本的なレビュー方針となる。

レビュー観点2:責務分離ができているか

ネスト関数が「単なるローカルスコープの関数」にとどまらず、他の処理と責務が混在している場合は分離すべきである。

def process_file(path):
    def read_file():
        with open(path) as f:
            return f.read()

    def parse(content):
        lines = content.split('\n')
        return [line.strip() for line in lines]

    content = read_file()
    return parse(content)

このようなコードは、read_fileparse がそれぞれ独立した関心事を扱っており、クラスやモジュール単位での再構成が望ましい。

Comment
@Reviewer: `read_file` や `parse` は汎用性の高い処理であり、外部関数やユーティリティモジュールに切り出すことでテストや再利用がしやすくなります。責務分離の観点から再構成を提案します。

レビュー観点3:再利用性とテスト容易性

ネスト関数のもう一つの欠点は、関数単位のテストが難しいことにある。外部スコープからアクセスできないため、ユニットテストが不可能になり、結果としてE2Eテストに依存しがちになる。

レビュー時には「この関数を将来的にテストや再利用したいか?」を想定し、スコープ設計が妥当かを評価すること。

テスト不可能なネスト構造
def validate():
    def is_valid_email(email):
        return "@" in email
    ...

この is_valid_email を後に単体テストしたくなっても、構造上それが困難になる。

代替設計:ローカル関数からの昇格

テスト性や責務の観点から、ローカル関数をモジュールレベルに昇格させることが推奨される場面も多い。

改善構造
def is_valid_email(email: str) -> bool:
    return "@" in email

def validate_user(email: str):
    if not is_valid_email(email):
        return False
    return True
Comment
@Reviewer: `is_valid_email` は汎用的かつ再利用性が高いロジックのため、ネストではなくモジュール関数として分離した構造が望ましいと考えます。

チェックリスト:ネスト関数レビュー

結論:レビューアーが見るべき「関数の深さ」

関数のネスト構造は、Pythonの柔軟な構文が許す表現の一つではあるが、コード全体の読みやすさ・保守性・再利用性を損なうリスクがある。レビューアーは次の観点で設計意図を読み解く必要がある:

  • 関数の責務に一貫性があるか
  • 構造がスコープ意図と一致しているか
  • 将来的な拡張・テストが可能な構造か

形式的な「ネスト禁止」のような指摘ではなく、読解者・保守者の視点に立った構造的レビューを心がけることが、信頼されるレビューアーへの第一歩となる。