libフォルダ名修正
This commit is contained in:
parent
68026223ab
commit
0055c66731
116
docments/DIコンテナ.md
Normal file
116
docments/DIコンテナ.md
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
# [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()
|
||||||
|
```
|
||||||
|
|
||||||
|
|
@ -2,7 +2,30 @@ import logging
|
|||||||
import functools
|
import functools
|
||||||
from .singleton import Singleton
|
from .singleton import Singleton
|
||||||
|
|
||||||
|
# ANSIカラーコード定義
|
||||||
|
RESET = "\033[0m"
|
||||||
|
RED = "\033[31m"
|
||||||
|
YELLOW = "\033[33m"
|
||||||
|
GREEN = "\033[32m"
|
||||||
|
BLUE = "\033[34m"
|
||||||
|
|
||||||
|
class ColorFormatter(logging.Formatter):
|
||||||
|
COLORS = {
|
||||||
|
logging.DEBUG: BLUE,
|
||||||
|
logging.INFO: GREEN,
|
||||||
|
logging.WARNING: YELLOW,
|
||||||
|
logging.ERROR: RED,
|
||||||
|
logging.CRITICAL: RED + "\033[1m", # 太字赤
|
||||||
|
}
|
||||||
|
|
||||||
|
def format(self, record):
|
||||||
|
color = self.COLORS.get(record.levelno, RESET)
|
||||||
|
message = super().format(record)
|
||||||
|
return f"{color}{message}{RESET}"
|
||||||
|
|
||||||
|
|
||||||
class CustomLogger(Singleton):
|
class CustomLogger(Singleton):
|
||||||
|
|
||||||
def __init__(self, name='main', log_file=None, level=logging.INFO):
|
def __init__(self, name='main', log_file=None, level=logging.INFO):
|
||||||
if hasattr(self, '_initialized') and self._initialized:
|
if hasattr(self, '_initialized') and self._initialized:
|
||||||
self.logger.setLevel(level)
|
self.logger.setLevel(level)
|
||||||
@ -12,10 +35,15 @@ class CustomLogger(Singleton):
|
|||||||
self.logger.setLevel(level)
|
self.logger.setLevel(level)
|
||||||
self.logger.propagate = False
|
self.logger.propagate = False
|
||||||
|
|
||||||
formatter = logging.Formatter(
|
# formatter = logging.Formatter(
|
||||||
|
# '%(asctime)s %(levelname)s [%(filename)s:%(lineno)3d]: %(message)s'
|
||||||
|
# )
|
||||||
|
formatter = ColorFormatter(
|
||||||
'%(asctime)s %(levelname)s [%(filename)s:%(lineno)3d]: %(message)s'
|
'%(asctime)s %(levelname)s [%(filename)s:%(lineno)3d]: %(message)s'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Console handler
|
# Console handler
|
||||||
ch = logging.StreamHandler()
|
ch = logging.StreamHandler()
|
||||||
ch.setFormatter(formatter)
|
ch.setFormatter(formatter)
|
22
src/lib/di_container.py
Normal file
22
src/lib/di_container.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
from .singleton import SingletonMeta
|
||||||
|
|
||||||
|
|
||||||
|
class DiContainer(metaclass=SingletonMeta):
|
||||||
|
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()
|
@ -18,3 +18,12 @@ class Singleton(object):
|
|||||||
if cls not in cls._instances: # ダブルチェック
|
if cls not in cls._instances: # ダブルチェック
|
||||||
cls._instances[cls] = super(Singleton, cls).__new__(cls)
|
cls._instances[cls] = super(Singleton, cls).__new__(cls)
|
||||||
return cls._instances[cls]
|
return cls._instances[cls]
|
||||||
|
|
||||||
|
|
||||||
|
class SingletonMeta(type):
|
||||||
|
_instances = {}
|
||||||
|
|
||||||
|
def __call__(cls, *args, **kwargs):
|
||||||
|
if cls not in cls._instances:
|
||||||
|
cls._instances[cls] = super().__call__(*args, **kwargs)
|
||||||
|
return cls._instances[cls]
|
Loading…
x
Reference in New Issue
Block a user