LoginSignup
27

More than 5 years have passed since last update.

posted at

PythonでDI(Dependency Injection)

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する

todo_usecase.py

@singleton
class TodoUseCase:

    def register(self, todo: Todo) - > None:
        print("call todo_usecase.register")

todo_controller.py

@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する

todo_repository.py

class TodoRepository:

    @abstractmethod
    def register(self, todo: Todo) -> None:
        raise NotImplementedError

todo_datasource.py

class TodoDataSource(TodoRepository):

    def register(self, todo: Todo) -> None:
        print("call todo_datasource.register")

todo_usecase.py

@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を指定することでもできるらしいです。

以上です。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
27