Pythonパッケージの公開APIを明確化する:__all__属性でコードの意図を伝える設計
サマリー
ビジネス価値: パッケージの意図的な設計により、開発チームの協業効率が向上し、保守性の高いシステム基盤を構築
開発効率: 明示的なAPI定義により、開発者が迷わずに適切なモジュールを利用でき、コードレビューの品質も向上
品質担保: Python言語仕様に準拠したコード構造により、静的解析ツールとの親和性が高まり、潜在的なバグを未然に防止
背景
図面評価システムの開発において、Pythonパッケージの構造が複雑化するにつれて、「どのモジュールが外部から利用されることを想定しているのか」「どの機能が内部実装なのか」という境界が曖昧になってきました。
特に、FastAPIを活用したWebアプリケーションでは、APIルーター、ドメインロジック、インフラストラクチャ層など、複数のレイヤーにまたがるパッケージ構造を持つため、開発者が「このモジュールをimportしても良いのか?」と迷うケースが頻発していました。
この課題を解決するため、Python言語の慣習に従った明示的なAPI定義を導入することにしました。
Before/After(擬似コード)
変更前:課題があるコード(Antipattern)
# api/__init__.py
# 空ファイル(何がパブリックAPIかが不明)
# api/v1/__init__.py
from fastapi import APIRouter
from .endpoints import health, evaluator
# ルーターを作成
api_router = APIRouter()
api_router.include_router(health.router, prefix="/health")
api_router.include_router(evaluator.router, prefix="/evaluator")
# どれが公開APIかが不明確
変更後:改善されたコード(Pattern)
# api/__init__.py
"""API layer package"""
# 明示的に「パブリックなオブジェクトは存在しない」ことを宣言
__all__: list[str] = []
# api/v1/__init__.py
"""API v1 router package"""
from fastapi import APIRouter
from .endpoints import health, evaluator
# ルーターを作成
api_router = APIRouter()
api_router.include_router(health.router, prefix="/health")
api_router.include_router(evaluator.router, prefix="/evaluator")
# パブリックAPIを明示的に宣言
__all__ = [
"api_router",
]
# api/v1/endpoints/__init__.py
"""API v1 endpoints package"""
# 詳細な説明コメントで設計意図を明確化
# このパッケージは内部モジュール群を含みますが、
# 外部からは api_router 経由での利用を想定しています
__all__: list[str] = []
技術的詳細
アーキテクチャ選定の思考プロセス
1. 他の手法との比較検討
当初、以下のような代替案も検討しました:
-
プライベートモジュール化(_プレフィックス): モジュール名にアンダースコアを付けることでプライベート化する手法
- 却下理由: 既存のディレクトリ構造を大幅に変更する必要があり、影響範囲が広すぎる
-
型チェッカー向けのstubファイル活用:
.pyiファイルでインターフェースを定義する手法- 却下理由: 運用コストが高く、実装と定義の同期が困難
2. __all__属性を選択した理由
- Python標準の慣習: PEP 8で推奨される、言語仕様に準拠したアプローチ
- ツールチェーンとの親和性: IDE、linter、型チェッカーが標準で対応
- 段階的導入: 既存コードを壊すことなく、パッケージ単位で順次適用可能
実装戦略
レイヤー別の適用方針:
domain/ → __all__ = [](ドメインオブジェクトは個別import推奨)
application/ → __all__ = [](アプリケーションサービスは直接importしない)
infrastructure/ → __all__ = [](インフラ実装は隠蔽)
api/v1/ → __all__ = ["api_router"](ルーターのみ公開)
この構造により、Clean Architectureの依存関係の向きを、import文レベルでも強制できるようになりました。
品質担保の仕組み
全ての変更に対して以下の自動チェックを実行:
- コードフォーマッター(Black): 一貫したコードスタイルの維持
- リンター(flake8): コーディング規約の遵守
- 型チェッカー(mypy): 型安全性の確保
- テストスイート: 網羅的な自動テストによる動作保証
学び・知見
再利用可能な設計原則
1. 明示性の原則
# Good: 意図が明確
__all__ = ["PublicClass"]
# Bad: 暗黙的
# __all__の定義なし(すべてがpublicに見える)
2. 段階的な情報開示
- パッケージレベルでの粗粒度な制御
- モジュールレベルでの細粒度な制御
- コメントによる設計意図の補完
3. ツールチェーン活用の重要性
単なるコーディング規約ではなく、開発ツールが自動で検証できる形式で制約を表現することで、レビューコストを削減し、品質を向上させることができます。
運用における教訓
- 空の__all__も価値がある: 「何もエクスポートしない」という意図の明示化
-
コメントでの補完:
__all__だけでは伝わらない設計思想を、コメントで補完することの重要性 -
型アノテーションの活用:
__all__: list[str] = []のように型情報も含めることで、開発体験が向上
免責事項
本記事は技術的知見の共有を目的としており、実際のプロジェクトから一部抽象化・簡略化した内容となっています。具体的なビジネス要件や制約により、最適解は変わる可能性があります。また、本記事はGitHub Pull Requestから自動で生成した下書きをベースに作成しました。
Generated by Claude AI on 2026-01-22 (UTC)