PythonでDIする
需要はなさそうですが、たまたまPythonでDIする方法を調べたので、、、
いくつかの選択肢
Injectは2015年で開発が止まっているようです。
di-pyはあまりドキュメントが充実していないようです。
siringaは型ヒントをごにょごにょしてInjectするちょっと変態チックなやり方だったので避けました。
消去法でInjectorを試してみました。
Injectorの特徴
ドキュメントでも説明されてますが、Google Guiceライクなフレームワークになっています。
Google Guiceの説明はこの方の記事がわかりやすかったです。
Google Guice 使い方メモ
InjectやModule、Providerなどの用語もGoogle Guiceとほぼ同じなので、先にGuiceを理解する方がわかりやすいかもしれません。
(やはり型ヒントがあるとはいえ、スクリプト言語でDIをするのは理解するまでがしんどかったです)
実装例
###インスタンスをDIする
@singleton
class TodoUseCase:
def register(self, todo: Todo) - > None:
print("call todo_usecase.register")
@singleton
class TodoController:
@inject
def __init__(self, todo_usecase: TodoUseCase) -> None:
self.todo_usecase = todo_usecase
def create_todo(self, todo: Todo) -> None:
self.todo_usecase.register(todo)
if __name__=='__main__':
injector = Injector()
todo_controller: TodoController = injector.get(TodoController)
todo: Todo = Todo()
todo_controller.create_todo(todo)
通常のインスタンスをDIする場合は@Inject
だけで勝手にDIしてくれます。
デフォルトではシングルトンではなく、@singleton
を付与することでシングルトンになるらしいです。(Guiceもデフォルトではシングルトンではない)
###実装クラスのインスタンスをDIする
class TodoRepository:
@abstractmethod
def register(self, todo: Todo) -> None:
raise NotImplementedError
class TodoDataSource(TodoRepository):
def register(self, todo: Todo) -> None:
print("call todo_datasource.register")
@singleton
class TodoUseCase:
@inject
def __init__(self, todo_repository: TodoRepository) -> None:
self.todo_repository = todo_repository
def register(self, todo: Todo) -> None:
self.todo_repository.register(todo=todo)
class TodoDIModule(Module):
def configure(self, binder):
binder.bind(TodoRepository, to=TodoDataSource, scope=singleton)
if __name__=='__main__':
injector = Injector([TodoDIModule()])
todo_use_case: TodoUseCase = injector.get(TodoUseCase)
todo: Todo = Todo()
todo_use_case.register(todo)
ポイントはModuleの実装クラスでDIの設定をするところです。
シングルトンにする方法はModuleでscopeを指定することでもできるらしいです。
以上です。