# [Python]DIコンテナの利用ガイドライン ## DIコンテナのメリット - 依存性の解決を通じてクラスの自立性を高める - テストしやすくなる - 構成情報をコンテナで一括管理できる - モジュール化により小規模コードの再利用性が向上 ## ユースケース - Logger や Config などの共通サービスの渡し込み - DBクライアントなどの再利用 - サービス層の分離 - Flask/FastAPI との統合 ## ライブラリの選定 ### punq - 最簡易な軽量DIコンテナ - Python標準の型ヒントで自動組み立て - 小規模スクリプトに最適 ```py import punq class Logger: def log(self, msg): print(f"[LOG] {msg}") class Service: def __init__(self, logger: Logger): self.logger = logger def run(self): self.logger.log("Running service!") # コンテナに登録 container = punq.Container() container.register(Logger) container.register(Service) # 自動で依存解決してインスタンス化 svc = container.resolve(Service) svc.run() ``` ### injector - Google Guice に着想を得た正統派DI - singleton scope や module 構築が可能 - Flask/FastAPI との連携実績が多い - 中央管理型プロジェクトに向いている ## 機能比較表 | 機能カテゴリ | 内容例 | punq | injector | | ---------------------- | ------------------------------------ | ---- | -------- | | 型ヒントによる自動解決 | `__init__(db: DBClient)`のような注入 | ✅ | ✅ | | スコープ管理 | singleton, transient の切り替え | ❌ | ✅ | | 明示的なバインディング | `register(ILogger, ConsoleLogger)` | ✅ | ✅ | | モジュール構造化 | DI定義をクラス単位で管理 | ❌ | ✅ | | 循環依存の検出 | 循環関係を検出・警告 | ❌ | △ | | テスト用差し替え | mock の注入や切替 | ✅ | ✅ | | パフォーマンス | 実装が軽く高速 | 高速 | 中程度 | ## 独自実装する場合のサンプルコード 以下はシンプルなDIコンテナの実装例で、singletonなインスタンス管理に対応した構造です。 ```python class Container: def __init__(self): self._registrations = {} self._singletons = {} def register(self, interface, implementation, singleton=True): self._registrations[interface] = (implementation, singleton) def resolve(self, interface): impl, singleton = self._registrations.get(interface, (None, None)) if impl is None: raise ValueError(f"No registration for {interface}") if singleton: if interface not in self._singletons: self._singletons[interface] = impl() return self._singletons[interface] else: return impl() # 利用例 class ILogger: def log(self, msg): raise NotImplementedError class ConsoleLogger(ILogger): def log(self, msg): print(f"[LOG] {msg}") class App: def __init__(self, logger: ILogger): self.logger = logger def run(self): self.logger.log("App is running") # DIコンテナの使用 container = Container() container.register(ILogger, ConsoleLogger, singleton=True) logger = container.resolve(ILogger) app = App(logger) app.run() ```