質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
DI (Dependence Injection)

DI (Dependence Injection)は、「依存性の注入」という概念を指します。オブジェクト間で依存性のあるコードを外部の設定ファイルから注入するソフトウェアパターン設計思想です。

Flask

FlaskはPython用のマイクロフレームワークであり、Werkzeug・Jinja 2・good intentionsをベースにしています。

Pythonista

Pythonistaは、iOS上でPythonプログラミングができる開発アプリです。さらに、Pythonの関数・変数などを自動で補完する便利なコードエディタや、PythonスクリプトをiOS上で多様な形で機能させる各種機能も内包しています。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

3回答

1890閲覧

Flask_Injectorを使用してDIしたいけどできない

__TakeTakeTake

総合スコア0

DI (Dependence Injection)

DI (Dependence Injection)は、「依存性の注入」という概念を指します。オブジェクト間で依存性のあるコードを外部の設定ファイルから注入するソフトウェアパターン設計思想です。

Flask

FlaskはPython用のマイクロフレームワークであり、Werkzeug・Jinja 2・good intentionsをベースにしています。

Pythonista

Pythonistaは、iOS上でPythonプログラミングができる開発アプリです。さらに、Pythonの関数・変数などを自動で補完する便利なコードエディタや、PythonスクリプトをiOS上で多様な形で機能させる各種機能も内包しています。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/04/15 11:31

前提・実現したいこと

Flask-Injectorを使用してDIをしたいんですが、うまくDIされていなくて困っています。誰かお助けください(python初心者なので、もしかしたら超初歩的なミスをしてるかもしれません)

発生している問題・エラーメッセージ

TypeError: __init__() missing 1 required positional argument: 'make_answer_usecase'

ディレクトリ構成

$ tree . . ├── README.md ├── __init__.py ├── __pycache__ │   └── __init__.cpython-38.pyc ├── adopter │   ├── __init__.py │   ├── __pycache__ │   │   └── __init__.cpython-38.pyc │   ├── controller │   │   ├── __init__.py │   │   ├── __pycache__ │   │   │   ├── __init__.cpython-38.pyc │   │   │   ├── controller_interface.cpython-38.pyc │   │   │   └── line_webhook_controller.cpython-38.pyc │   │   ├── controller_interface.py │   │   └── line_webhook_controller.py │   ├── gateway │   │   ├── __init__.py │   │   └── __pycache__ │   │   └── __init__.cpython-38.pyc │   └── presenter │   ├── __init__.py │   ├── __pycache__ │   │   ├── __init__.cpython-38.pyc │   │   └── line_presenter.cpython-38.pyc │   └── line_presenter.py ├── app.yaml ├── domain │   ├── __init__.py │   ├── __pycache__ │   │   ├── __init__.cpython-38.pyc │   │   └── corona_data.cpython-38.pyc │   └── corona_data.py ├── drivers │   ├── __init__.py │   ├── __pycache__ │   │   └── __init__.cpython-38.pyc │   └── repositories │   ├── __init__.py │   ├── __pycache__ │   │   ├── __init__.cpython-38.pyc │   │   └── datastore.cpython-38.pyc │   └── datastore.py ├── helper │   ├── __init__.py │   ├── __pycache__ │   │   ├── __init__.cpython-38.pyc │   │   ├── config.cpython-38.pyc │   │   ├── di_container.cpython-38.pyc │   │   ├── logging.cpython-38.pyc │   │   └── singleton.cpython-38.pyc │   ├── config.py │   ├── di_container.py │   ├── logging.py │   └── singleton.py ├── main.py ├── make_env.sh ├── requirements.txt └── usecase ├── __init__.py ├── __pycache__ │   └── __init__.cpython-38.pyc ├── impl │   ├── __init__.py │   ├── __pycache__ │   │   ├── __init__.cpython-38.pyc │   │   └── make_answer_usecase.cpython-38.pyc │   └── make_answer_usecase.py └── interfaces ├── __init__.py ├── __pycache__ │   ├── __init__.cpython-38.pyc │   ├── corona_data_repository.cpython-38.pyc │   ├── make_answer_input_port.cpython-38.pyc │   └── make_answer_presenter.cpython-38.pyc ├── corona_data_repository.py ├── make_answer_input_port.py └── make_answer_presenter.py

該当のソースコード

main.py

python

1import logging 2 3from flask import Flask, request 4from flask_injector import FlaskInjector 5from flask.views import View 6from injector import inject 7from linebot import ( 8 LineBotApi, 9 WebhookHandler 10) 11from linebot.exceptions import ( 12 InvalidSignatureError 13) 14from linebot.models import ( 15 MessageEvent, TextMessage 16) 17 18from helper import logging 19from helper import di_container 20from helper.config import Config 21from usecase.impl import make_answer_usecase 22from adopter.controller import line_webhook_controller 23 24 25 26app = Flask(__name__) 27 28logging.logging_config() 29c = Config() 30 31line_bot_api = LineBotApi(c.LINE_ACCESS_TOKEN) 32handler = WebhookHandler(c.LINE_CHANNEL_SECRET) 33 34 35@app.route("/callback", methods=['POST']) 36def callback(): 37 38 # get X-Line-Signature header value 39 signature = request.headers['X-Line-Signature'] 40 41 # get request body as text 42 body = request.get_data(as_text=True) 43 44 # handle webhook body 45 try: 46 handler.handle(body, signature) 47 except InvalidSignatureError: 48 print("Invalid signature. Please check your channel access token/channel secret.") 49 abort(400) 50 51 return 'OK' 52 53 54@handler.add(MessageEvent, message=TextMessage) 55def handle_message(event): 56 FlaskInjector(app=app, modules=[di_container.configure]) 57 lwc = line_webhook_controller.LineWebhookController() 58 c.REPLY_TOKEN = event.reply_token 59 lwc.serve(message=event.message.text) 60 61 62if __name__ == "__main__": 63 app.run(debug=c.DEBUG) 64

adopter.controller.line_webhook_controller.py

python

1from injector import inject 2from flask.views import View 3 4import sys 5sys.path.append('../../') 6from usecase.interfaces.make_answer_input_port import MakeAnswerInputPortInterface 7from helper.config import Config 8from adopter.controller.controller_interface import ControllerInterface 9 10class LineWebhookController(View, ControllerInterface): 11 @inject 12 def __init__(self, make_answer_usecase: MakeAnswerInputPortInterface): 13 self.make_answer_usecase = make_answer_usecase 14 15 def serve(self, message) -> str: 16 self.make_answer_usecase.make_answer(message)

usecase.impl.make_answer_usecase

python

1from datetime import datetime 2 3from injector import inject 4from jp_pref import prefecture 5from flask.views import View 6 7import sys 8sys.path.append('../../') 9from usecase.interfaces.corona_data_repository import CoronaDataRepositoryInterface 10from usecase.interfaces.make_answer_presenter import MakeAnswerPresenterInterface 11from domain import corona_data 12 13 14class MakeAnswerUseCase(View): 15 @inject 16 def __init__(self, repo: CoronaDataRepositoryInterface, presenter: MakeAnswerPresenterInterface): 17 """ 18 :param repo: 19 repoはコロナのデータが格納されているレポジトリ。 20 """ 21 self.repo = repo 22 self.presenter = presenter 23

helper.di_container.py

import sys sys.path.append("../") from usecase.interfaces.make_answer_input_port import MakeAnswerInputPortInterface from usecase.interfaces.make_answer_presenter import MakeAnswerPresenterInterface from usecase.interfaces.corona_data_repository import CoronaDataRepositoryInterface from usecase.impl.make_answer_usecase import MakeAnswerUseCase from drivers.repositories.datastore import DatastoreRepository from adopter.presenter.line_presenter import LinePresenter from adopter.controller.controller_interface import ControllerInterface from adopter.controller.line_webhook_controller import LineWebhookController from adopter.controller.line_webhook_controller import MockController # DIの設定はここに集約している。使用する実装クラスを変更する場合はここを変える。 def configure(binder) -> None: # Controllerが持つUsecaseの設定 binder.bind(MakeAnswerInputPortInterface, to=MakeAnswerUseCase, scope=request) # Usecaseが持つPresenterの設定 binder.bind(MakeAnswerPresenterInterface, to=LinePresenter) # Usecaseが持つRepositoryの設定 binder.bind(CoronaDataRepositoryInterface, to=DatastoreRepository)

試したこと

Flask-Injectorのドキュメント(https://pypi.org/project/Flask-Injector/)に、

# Initialize Flask-Injector. This needs to be run *after* you attached all # views, handlers, context processors and template globals. FlaskInjector(app=app, modules=[configure])

とあったので、injectしたいクラスが入ってるパッケージを全てmain.pyのなかでインポートして、FlaskInjector(...)より先に持ってきました。でも無理でした。

補足情報(FW/ツールのバージョンなど)

Flask-Injector == 0.12.3
python == 3.8.5

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

Injector, Flask-Injectorは初めて知ったので、少しドキュメントを見た感じですが、「Welcome to Injector’s documentation! - Injector 0.18.4 documentation」のページに以下のようにあります(太字は独自に修飾を追加)。

No global state - you can have as many Injector instances as you like, each with a different configuration and each with different objects in different scopes. Code like this won’t work for this very reason:

class MyClass: @inject def __init__(t: SomeType): # ... MyClass()

This is simply because there’s no global Injector to use. You need to be explicit and use Injector.get, Injector.create_object or inject MyClass into the place that needs it.

injectされるクラスを使いたいとき、基本的な使い方では、直接「MyClass()」のようにインスタンス化するのではなく、「Injector.get(~)」「Injector.create_object(~)」のようにしてインスタンスを生成するのではないでしょうか?

また、GitHub - alecthomas/flask_injector: Adds Injector support to Flask.では、「Features」として以下のように機能を紹介しています。

Features

Flask-Injector lets you inject dependencies into:

  • views (functions and class-based)
  • before_request handlers
  • after_request handlers
  • teardown_request handlers
  • template context processors
  • error handlers
  • Jinja environment globals (functions in app.jinja_env.globals)
  • Flask-RESTFul Resource constructors
  • Flask-RestPlus Resource constructors
  • Flask-RESTX Resource constructors

Flask-Injector supports defining types using function annotations (Python 3), see below.

上記ページの説明を読むと、Featuresにある処理(views, before_request handlerなど)では、「Flask-Injector」独自の機能として、「@inject」や「binder.bind(~)」などを使って定義した内容を自動的に解決してくれるように見えました。その際には、function annotationsで定義した内容をサポートしているようです。

質問文のコードでは「line_webhook_controller.LineWebhookController()」と直接インスタンス化しようとしていること、そのコードが(injectorで依存関係を解決しようとしている箇所が)上記のFlask処理ではないことから、エラーが発生しているように見えます。

すみませんが、Injector、Flask-Injectorともに使ったことはないので、的外れでしたらごめんなさい。

投稿2021/04/19 19:11

msiz07

総合スコア172

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

このエラーは次のコードで発生するエラーと同じエラーではないでしょうか。

Python

1>>> from injector import Injector, inject 2>>> class Inner: 3... def __init__(self): 4... self.forty_two = 42 5... 6>>> class Outer: 7... @inject 8... def __init__(self, inner: Inner): 9... self.inner = inner 10... 11>>> injector = Injector() 12>>> outer = Outer() 13Traceback (most recent call last): 14 File "<stdin>", line 1, in <module> 15TypeError: __init__() missing 1 required positional argument: 'inner'

このコードは Injector ライブラリの A Quick Example のコードを改変したものです。
次のコードが元のコードです。

Python

1>>> from injector import Injector, inject 2>>> class Inner: 3... def __init__(self): 4... self.forty_two = 42 5... 6>>> class Outer: 7... @inject 8... def __init__(self, inner: Inner): 9... self.inner = inner 10... 11>>> injector = Injector() 12>>> outer = injector.get(Outer) 13>>> outer.inner.forty_two 1442

このエラーについては、Injector ライブラリの Frequently Asked Questions の中に簡単な説明があります。

(説明文中の "see its documentation for usage notes" とはこちらのドキュメントでしょうか。)

投稿2021/04/18 23:48

etherbeg

総合スコア1195

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

TypeError: xxx missing x required positional argument: ‘xxxxxx’は、関数やメソッドが、伴うべき引数(正確に言えば位置引数)を伴わず使われた場合に表示されるエラーメッセージです。

このエラーメッセージを表示させるのは簡単。

Python

1# 引数 my_name を使用する関数 my_func を定義 2>>> def my_func(my_name): 3... print("Hello,", my_name) 4... 5# 引数を伴わずに my_func 関数を使用 6>>> my_func() 7Traceback (most recent call last): 8 File "<stdin>", line 1, in <module> 9TypeError: my_func() missing 1 required positional argument: 'my_name' 10 11# インスタンス化の際に引数 my_name を使用するクラス MyClass を定義 12>>> class MyClass: 13... def __init__(self, my_name): 14... self.my_name = my_name 15... def greet(self): 16... print("Hello,", self.my_name) 17... 18# 引数を伴わずに MyClass クラスをインスタンス化 19>>> my_class = MyClass() 20Traceback (most recent call last): 21 File "<stdin>", line 1, in <module> 22TypeError: __init__() missing 1 required positional argument: 'my_name'

いずれも本来なら

Python

1>>> my_func("World") 2Hello, World 3>>> my_class = MyClass("World") 4>>> my_class.greet() 5Hello, World

と引数を伴って使うべきものです。

TypeError: __init__() missing 1 required positional argument: 'make_answer_usecase’というエラーメッセージは、「(クラスの初期化メソッドである) __init__() というメソッドで必須である位置引数が1個欠けています。その引数はメソッド定義の中で 'make_answer_usecase' という名前で定義されている引数です」と言っています。

掲出のプログラムの中に、’make_answer_usecase’ という名前の引数を伴って初期化メソッドが定義されているクラス定義はあったでしょうか?
ありました。adopter.controller.line_webhook_controller.py の中のLineWebhookControllerクラス。

Python

1class LineWebhookController(View, ControllerInterface): 2 @inject 3 def __init__(self, make_answer_usecase: MakeAnswerInputPortInterface): # <= ここ 4 self.make_answer_usecase = make_answer_usecase 5 6 def serve(self, message) -> str: 7 self.make_answer_usecase.make_answer(message)

このクラス定義から、LineWebhookControllerクラスからインスタンスを作る際は、MakeAnswerInputPortInterfaceクラスのインスタンスを引数に伴って作るべき、ということがわかります。

では、掲出のプログラムの中に、LineWebhookControllerクラスからインスタンスを作っている箇所はあったでしょうか?
ありました。main.py の中の次の部分。

Python

1def handle_message(event): 2 FlaskInjector(app=app, modules=[di_container.configure]) 3 lwc = line_webhook_controller.LineWebhookController() # <= ここ 4 c.REPLY_TOKEN = event.reply_token 5 lwc.serve(message=event.message.text)

lwc = line_webhook_controller.LineWebhookController()と引数を伴わずに使われています。おそらくここでエラーが発生しています。エラーメッセージの全文を見ないと確かなことは言えませんが、おそらく間違いないでしょう。


ちなみに、質問する際には、エラーメッセージの一部を抜き出して貼るのではなく、エラーメッセージの全文を、つまりTraceback (most recent call last):から始まる行をすべて貼るべきです。そこにはエラーが発生したファイル名、ファイル中のエラー発生箇所(行番号)がすべて記録されています。

なので、質問しない場合でも、エラーメッセージには全文に丹念に目を通すといいです。

投稿2021/04/16 22:48

編集2021/04/16 22:59
etherbeg

総合スコア1195

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

__TakeTakeTake

2021/04/18 03:33

ご回答ありがとうございます。ご指摘の通り、スタックトレース全てを貼るべきでした。申し訳ございません。 確かに `LineWebhookController` は引数を伴わずにインスタンス化しているのですが、FlaskInjector()によりDIしているので、自動的に`MakeAnswerInputPortInterface`インターフェースを満たしていて、かつdi_container.pyで定義している `MakeAnswerUsecaseクラス` が引数に渡されるといった認識でした。それなのに何故、`TypeError: __init__() missing 1 required positional argument: 'make_answer_usecase’`のようなエラーが出るのかわからないといった質問です。 説明が足りず申し訳ありませんが、何か知見があればお貸しいただけると幸いです。
etherbeg

2021/04/18 23:54

この度は私の無知により見当違いの回答をしてしまい申し訳ありませんでした。 お話にもならない回答と無視されても当然なところ、やんわりと無知を指摘していただき、勉強の機会をいただいたことを感謝いたします。 新たに回答を書き込ませていただきましたので、ご覧いただけますと幸いです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問