Python|__init__.py依存整理でサブパッケージ設計を整える方法
この記事のポイント
- init.py設計の暗黙的な依存肥大を読み解く力が身につく
- サブパッケージ構造をレビュー視点で整理できる
- モジュール境界・責務分離・公開API設計を体系化できる
- 実務レビューで設計臭を検出する技法を学べる
そもそも__init__.pyとは
Pythonにおける__init__.pyは、パッケージ認識・初期化制御・エクスポート整理という3つの役割を持つファイルです。
- ディレクトリがパッケージと認識される(Python3.3以降は暗黙的だが依然慣例として残る)
- サブモジュールの初期化処理(副作用コード含む)が実行可能
- サブモジュール・クラス・関数をエクスポート整理
典型的__init__.py使用例
# services/__init__.py
from .user_service import UserService
from .order_service import OrderService
__all__ = ["UserService", "OrderService"]これにより、呼び出し側では以下のように書けます。
from services import UserServiceなぜこれをレビューするのか
init.pyは便利すぎる反面、設計崩壊の入口になりやすい領域です。レビューアーは以下のような依存設計の歪みを読み解く責任があります。
レビューアー視点
- init.py肥大化による循環依存リスクを検出する
- エクスポート範囲の適切性を確認する
- パッケージ境界の責務分離が破綻していないか確認する
- 内部実装と公開APIの整理状況を読む
- 再帰的インポートが隠れていないか確認する
開発者視点
- from-importが楽で__init__.pyに次々追加してしまう
- API公開意識が希薄なまま「全部import」になりやすい
- 循環importのエラーが出てから慌てて整理を始める
- テスト実行時に副作用実行が発生して気づく
init.py設計が崩壊する構造
init.py肥大化の典型症状
- サブモジュール全てをimportして束ねるだけの巨大エクスポーター化
- 実行時初期化コードが__init__.pyに混入
- 順序依存importが生じ循環依存を誘発
- どのモジュールが利用可能なのか可視性が消失
- 境界責務が不明瞭になる
実際の崩壊コードを例示します。
肥大化__init__.py
# services/__init__.py
from .user_service import UserService, create_user, delete_user
from .order_service import OrderService, create_order, cancel_order
from .payment_service import PaymentService, process_payment
__all__ = [
"UserService", "create_user", "delete_user",
"OrderService", "create_order", "cancel_order",
"PaymentService", "process_payment"
]
@Reviewer全モジュール統合が続いており依存膨張が進行中です。公開API層を分離しましょう。
init.py崩壊の依存構造モデル
良い実装例:公開API層分離
設計肥大を防ぐには「エクスポート層(facade)と内部層を分離」が有効です。
ディレクトリ構造
services/
__init__.py
api.py
user_service.py
order_service.py
payment_service.py__init__.pyを薄く保つ
# services/__init__.py
from .api import *api.pyに公開API集中
# services/api.py
from .user_service import UserService, create_user, delete_user
from .order_service import OrderService, create_order, cancel_order
from .payment_service import PaymentService, process_payment
__all__ = [
"UserService", "create_user", "delete_user",
"OrderService", "create_order", "cancel_order",
"PaymentService", "process_payment"
]改善ポイント
- init.pyは完全に薄く保持
- 公開APIをapi.pyで集中管理
- 内部依存を隠蔽可能
- 循環依存発生確率が劇的低下
レビュー文化は「init.pyを薄く保て」で統一すると崩壊防止力が高まります。
良くない実装例: ケース1(副作用混入)
__init__.pyに副作用
# services/__init__.py
from .user_service import UserService
# グローバル初期化副作用
UserService.initialize_global_connection()
@Reviewer__init__.pyに副作用処理が混入しています。import時副作用は避け、明示実行に分離してください。
問題点
- テスト時に想定外初期化が発生
- 依存先の初期化順序制御が困難化
良くない実装例: ケース2(import漏れ誘発)
エクスポート漏れリスク
# services/__init__.py
from .user_service import UserService
# order_serviceのimport忘れ発生
@ReviewerAPI公開管理が散在し、import漏れの温床になります。集中公開層を導入してください。
問題点
- API追加時に複数ファイルの変更作業が必要になる
- import漏れがレビューで検出しづらい
改善例:責務整理型設計
集中API層版
# services/api.py
from .user_service import UserService
from .order_service import OrderService
from .payment_service import PaymentService
__all__ = [
"UserService",
"OrderService",
"PaymentService",
]PlantUML:責務整理構造イメージ
観点チェックリスト
まとめ
init.pyは便利な道具ですが「便利すぎる設計臭の源泉」にもなりやすい領域です。レビューアーは「パッケージの入口構造を制御できているか?」という視点を常に持ち続けることが、Python設計レビュー文化の中核になります。

