libフォルダ名修正

This commit is contained in:
ry.yamafuji 2025-08-30 14:30:20 +09:00
parent 68026223ab
commit 0055c66731
7 changed files with 176 additions and 1 deletions

116
docments/DIコンテナ.md Normal file
View 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()
```

View File

@ -2,7 +2,30 @@ import logging
import functools
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):
def __init__(self, name='main', log_file=None, level=logging.INFO):
if hasattr(self, '_initialized') and self._initialized:
self.logger.setLevel(level)
@ -12,10 +35,15 @@ class CustomLogger(Singleton):
self.logger.setLevel(level)
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'
)
# Console handler
ch = logging.StreamHandler()
ch.setFormatter(formatter)

22
src/lib/di_container.py Normal file
View 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()

View File

@ -18,3 +18,12 @@ class Singleton(object):
if cls not in cls._instances: # ダブルチェック
cls._instances[cls] = super(Singleton, cls).__new__(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]