117 lines
3.6 KiB
Markdown
117 lines
3.6 KiB
Markdown
# [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()
|
|
```
|
|
|
|
|