【FastAPI】Dependsについて
需要ないかもだけど、そもそも依存性なんぞな人間が、FastAPIでDependsと関数呼び出しとかで混乱したので(理解とかじゃなく、実装時にあれれってなった感じ)メモ
依存性(依存関係)、依存性注入(D/I)について
まあ、関数呼び出しと何がちゃうねんと思ってたけど、理解はともかくなんとなく、あぁそういうことね、って思ったり忘れたりするのでメモ
※D/I一般かFastAPI固有かもちょっと整理できてないけどご了承の上お読みください。
Path operation関数 (@でデコレートしてパスと直下の関数をセットにするやつ)とかの定義時に、引数としてDepends(呼び出し可能なものcallable)という形で設置してやることで、依存する関数などの呼び出し可能オブジェクトの引数などを全て列挙しなくてよい(もちろん引数を与える側はちゃんとその引数を理解して渡す必要がある)
要は、(まあ公式のまんまなんだけど)必要なパラメータなど依存するものの定義をいちいち何度も書かなくて良いよ、という実装(開発者)側の都合の話です。この辺りは、概要とか公式ドキュで読んで、その時は、あっそって思ったけど、いざ実装ってなった時、「???」ってなりがちかも
こういうことって、まあよくあるよね(私だけかもしれんけど)
公式
You only give Depends a single parameter.
This parameter must be something like a function.
And that function takes parameters in the same way that path operation functions do.
Tip
You'll see what other "things", apart from functions, can be used as dependencies in the next chapter.
Whenever a new request arrives, FastAPI will take care of:
Calling your dependency ("dependable") function with the correct parameters.
Get the result from your function.
Assign that result to the parameter in your path operation function.
common_parameters
/items/
/users/
This way you write shared code once and FastAPI takes care of calling it for your path operations.
和訳
Dependsは1つのパラメーターのみを指定します。
このパラメータは関数のようなものでなければなりません。
そして、その関数は、パス操作関数と同じ方法でパラメーターを受け取ります。
ヒント
次の章では、関数以外の他の「もの」を依存関係として使用できることを確認します。
新しいリクエストが到着するたびに、FastAPIが次の処理を行います。
正しいパラメーターを使用して依存関係(「信頼できる」)関数を呼び出す。
関数から結果を取得します。
その結果をパス操作関数のパラメーターに割り当てます。
関数呼び出しとの違い
こっからは、自分の浅い理解によるもので、明確なドキュメント参照などはありません。
当たり前のことかつ以下の見解は大いに間違っているかもですが、とりあえず自分の整理のために・・・
関数呼び出し(あるいは呼び出し可能callableなオブジェクト)を呼び出して使うのではなく、(根本を掘ったら同根かもしれないけれど、)Javaとか(そういえばPythonにもあるか)の「継承」に似たものと考えている。
継承関係(みたいなもの?)を簡単に書けるようにしたのが、FastAPIのDependsとイメージしてます。
なので、「他人」である関数などを呼び出して使うというより、依存先と同様の機構を「自分」が備えている、というイメージ。
「だからなんなんだ、イメージとかメタファー(にすらなっていないけれど)ではなくて、機能上の違いを説明しろ」と言われると困ってしまうが、
今のところ、関数呼び出しなどでは、呼び出した側が引数を与えられるけれど、依存関係では、むしろ自分が呼び出されるときに依存先(継承元)と同様の引数などを要求する立場にある、というのが、実装上、外観からみてすぐにわかる違いだと考えている。
本質的でもなく、概念的にも的外れかもしれないけれど、とりあえず勉強用、備忘録として、ご了承ください。
追記_20220915
公式にもある下記のような記述(処理)について、
わかりやすい説明があったので参考
db_session: Session = Depends(get_db)
多分、こんな感じ・・・
直接上記の件ではないけれど、関数定義時の(仮)引数〔argument〕に、初期値として直接関数(の戻り値)を渡すのではなく、Dependsを渡すことによって、(初期値に関数の戻り値を渡していれば不要になるところを)、この当該の関数を呼び出した時に、呼び出した側で、引数指定の関数なり実行後戻り値のインスタンスなりを渡すことを強制させられる。
したがって、Dependsによって(実)引数〔parameter〕を省略できるのではなく、むしろ「依存している」クラスや関数を明記し、(呼び出し元に対して)引数を(渡すことを)強制させることができる。
追記_20220920
よく関数(メソッド)やクラスの定義の際に、特定のクラスへの「依存」を減らして、外部化することが推奨される。
すなわち、特定のクラスなどを、当該のクラス内で宣言して(呼び出して)、インスタンスを生成すると、テスト時などにそれらを切り出して単体でテストしたり、あるいは、改修などを行うときの影響範囲の切り分けなどが困難になるため、「自律性・独立性」を高めることで、「オブジェクト」間の関係を「疎結合」にすることが奨められるように思う。
この際、言われるのが、依存している(つまり当該オブジェクト内部で呼び出したり生成したりしている)対象について、(その対象ではなく)その依存性をインターフェース化して、外部から引数として渡す、という形を取ることによって、それを実現するというものだろう。
つまり、依存している(内部で利用している)オブジェクトから必要な要素を抽象して、引数として外部委託する。
この際、FastAPIのDependsを用いることで、必要な引数などをDependsの引数に渡したクラスや関数から自動で判別してくれる、というものと考えている。つまり、(だいぶ違うと思うけど)依存オブジェクトの自動インターフェース化(インターフェースの代理)を担ってくれる、と考えたらわかりやすいかもしれない。
今後修正、訂正はありうる。
追記_20221002
関数やクラスをラッピングする時に、引数に、そのラッピングしたい関数の引数を全て書くのが大変、もしいろんなバリエーションでラッピングしたい場合は、毎回その引数を新しい関数の定義に書かなければいけな区なってしまう。
そんな時に、引数のセットに名前をつけて、その名前を(仮)引数として書いておくことで、必要な引数を指定できるとしたら、この定義ごとに仮引数を全て書く必要がある、というめんどくさい問題を解消できる。
それを実現してくれているのが、このDependsと考えると、なんとなくスッキリする。
要は、(仮)引数にラベリングして、そのラベルで(仮)引数を記載できる。
仮引数(parameter)はそもそも、実引数(argument)の検証のための機構だと考えると、これは、実行時の話ではなくて、関数やクラスのラッピングとか「依存」(関係構築)時に、その書き方を楽にしてくれる配慮、ということであると思える。
依存関係とはいうけれど、結局それは、ある関数とかクラスを用意する時に既存の関数とかクラスなどを流用して、それらを(あくまで流用なので、非破壊的に、つまり値渡しのように)上書くことである。
つまりある関数やクラスを元にした(流用、利用した)拡張版の関数やクラス、何らかの呼び出し可能オブジェクトを作成(定義)する際に、元の関数やクラスの情報を何回も書かなければいけない手間を省く、そういった機構である、と現在は見ている。
もちろん何らかの関数やクラスに「依存」した関数やクラスを作成する際も、毎回「依存先の」関数やクラスの定義を書いているわけではなく、そのシグネチャのみ関数名やクラス名と引数のみを記載すれば問題ないわけだが、さらにその(仮)引数についても省略するための処方であり、検証する仮引数の明示をしないで、その検証をFastAPI側が担うことで、引数の型チェックの簡易化を提供している。