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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Flask

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

Python

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

Q&A

解決済

3回答

4400閲覧

Flaskのデコレータの仕組みが分からない

GOU_KUN

総合スコア11

Flask

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

Python

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

1グッド

1クリップ

投稿2021/09/08 10:29

前提・実現したいこと

初歩的な質問かもしれませんがすみません。
Flaskのような、デコレータを使って簡単にプログラムを作成できるようなプログラム(ライブラリ)を作りたいです。
デコレータでラップされた関数を同じファイルから直接実行することなく、別ファイルのライブラリのほうから実行できるようにするということです。

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

python

1# coding: utf-8 2from flask import Flask, render_template 3 4app = Flask(__name__) 5 6@app.route("/") 7def index(): 8 return render_template("index.html") 9 10if __name__ == "__main__": 11 # webサーバー立ち上げ 12 app.run(debug=False, host='0.0.0.0', port=80)

app.routeでラップされたindex関数を実行したわけではないのに、何故app.runだけで実行できてしまうのかがわかりません。indexはどこから呼び出されているのでしょうか。

試したこと

Flaskのコードを直接見てみましたが、ほとんどわかりませんでした。
こちらの記事を見つけ、見てみましたが、

デコレーターが設定された関数が実行され。そこでreturnされた値が表示される。

とだけしか書いておらず、どのような仕組みで実行されているのかはよくわかりませんでした。


初歩的な質問かもしれませんが、ご教授いただければ幸いです。

glyzinieh👍を押しています

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

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

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

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

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

GOU_KUN

2021/09/08 10:52

返信ありがとうございます。ご提示いただいた記事を読んでみたのですが、やはりどのように関数が実行されるかはわからないままでした。 自分でももっと調べたいと思います。
guest

回答3

0

ベストアンサー

まず関数が"オブジェクト"であることが分かっている必要があります。

1.関数名は、def文を実行した名前空間に束縛されている名前(=いわゆる変数)である
2.なので関数名を評価すると関数オブジェクトになる
3.関数オブジェクトは(オブジェクトなので)データ構造で取り扱える
4.関数オブジェクトに続けて括弧と引数(...)で呼びだせる

の理解がまず必要です。

python

1def func1(): 2 print("done func1") 3 4def func2(): 5 print("done func2") 6 7funcs = [func1, func1, func2] 8for f in funcs: 9 f()

結果

plain

1done func1 2done func1 3done func2

次が

5.関数はオブジェクトなので関数を返す関数が書ける
6.関数はオブジェクトなので関数に関数を渡せる

の理解です。

python

1def create_func(arg_outer): 2 def func(arg_inner): 3 print(f'「{arg_outer}を引数にして呼んだcreate_funcが作った関数」を{arg_inner}を引数にして呼んだ') 4 return func 5 6f1 = create_func('あれ') 7f1('それ') 8 9def do_twice(f): 10 """関数オブジェクトを受け取って2回、別の引数で呼び出す""" 11 f('1回目') 12 f('2回目') 13 14f2 = create_func('例えばこれ') 15do_twice(f2)

実行結果

plain

1「あれを引数にして呼んだcreate_funcが作った関数」をそれを引数にして呼んだ 2「例えばこれを引数にして呼んだcreate_funcが作った関数」を1回目を引数にして呼んだ 3「例えばこれを引数にして呼んだcreate_funcが作った関数」を2回目を引数にして呼んだ

デコレータは5.と6.の複合による使い方で、Flaskのような仕組みは3.と4.によるものです。
この2つにはすごくギャップがありますが、かんたんにグローバル変数を使った構造で組み立てるとこんな感じになります。

python

1path_to_function_dict = {} 2 3 4def route(path): 5 def decorator(f): 6 path_to_function_dict[path] = f 7 return f 8 9 return decorator 10 11 12def call_some_function(path, *args, **kwargs): 13 if path in path_to_function_dict: 14 return path_to_function_dict[path](*args, **kwargs) 15 16 raise RuntimeError 17 18 19@route('/index') 20def index(): 21 print(f'indexを呼び出したよ') 22 23 24@route('/do') 25def do(s): 26 print(f'{s}を引数にdoを呼び出したよ') 27 28 29# ここから下ではindexやdoという関数名を使うことなく呼び出せる 30 31 32call_some_function('/index') 33call_some_function('/do', s='例えばこんな値')

実行結果

plain

1indexを呼び出したよ 2例えばこんな値を引数にdoを呼び出したよ

このどこかに、?? となっている箇所があるのだと思いますが、それがどのあたりなのかは分かりません。


(追記)

https://docs.python.org/ja/3/reference/compound_stmts.html#index-20

@f1(arg)
@f2
def func(): pass

は、だいたい次と等価です

def func(): pass
func = f1(arg)(f2(func))

とあります。つまり、

@route('/index') def index(): print(f'indexを呼び出したよ')

def index(): print(f'indexを呼び出したよ') index = route('/index')(index)

だいたい等価なのです。
routeを引数付きで呼び出す方が先に実行されるのです。

投稿2021/09/08 12:56

編集2021/09/08 15:33
quickquip

総合スコア11038

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

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

GOU_KUN

2021/09/08 13:45

なるほど! わざわざご丁寧に詳しく説明してくださりありがとうございます! ベストアンサーにさせて頂きたいのですが、「??」となっている所があるので質問してもいいですか?一番最後のところなのですが、 「route関数は実行されているのか」 ということです。関数は定義しただけでは実行されないので、この状態だとpath_to_function_dictは空なのではないか、と思ったんです。ラップすると実行されるんですかね?(??) 公式ドキュメントの読み方もよく分かっておらず、知識不足ですみません……
GOU_KUN

2021/09/08 21:38

わかりやすく追記してくださりありがとうございます。 わざわざ時間を割いて丁寧に回答していただきとても助かりました! 具体的なわかりやすいコードをご提示頂いたため、ベストアンサーにさせてもらいました。 ppaulさんやTakaiYさん、コメントをくださったikapyさんも、回答してくださり、本当にありがとうございました!
guest

0

質問読んで気になって調べたので、いちおう書いておきます。

Flaskのソースで、「@app.route()」を定義しているのは以下の場所です。

appはFlaskのインスタンスです。

app = Flask(name)

FlaskクラスはScaffoldクラスを継承しています。

class Flask(Scaffold):

Scaffoldクラスにroute関数が定義されていて、これがデコレータです。

def route(self, rule: str, **options: t.Any) -> t.Callable:

routeの実装の中で、登録する関数が呼ばれています。

self.add_url_rule(rule, endpoint, f, **options)

Scaffoldのadd_url_rule関数は仮想メソッドなので、実体はFlaskクラスに実装になります。

@setupmethod

def add_url_rule( self, rule: str, endpoint: t.Optional[str] = None, view_func: t.Optional[t.Callable] = None, provide_automatic_options: t.Optional[bool] = None, **options: t.Any, ) -> None:

ここでindex関数が、ppaulさんの言うように、ルートの処理呼び出しテーブルに登録されます。

この先はこんな風に処理を追いかけていけばOKです。

まあ、まったく初歩的なことではないので、どのような仕組みで実現されているかの理解も、それを応用した処理を作るのもかなり大変だと思いますが、頑張ってください。

投稿2021/09/08 13:15

TakaiY

総合スコア12765

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

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

GOU_KUN

2021/09/08 13:54

わざわざ調べてくださりありがとうございます! add_url_rule関数は仮想メソッドなんですね。そもそも仮想メソッドというのを知りませんでした…(笑)勉強になります。 quickquipさんのお陰で問題は解決しそうですが、Flaskの内部的な処理も気になるので、頑張って調べていきたいです。
guest

0

  • デコレータの仕組み

関数デコレータの動作については、公式ドキュメント 8.6. 関数定義 をお読みください。

「関数定義は一つ以上の デコレータ 式でラップできます。」で始まる部分です。

  • app.routeでラップされたindex関数を実行したわけではないのに、何故app.runだけで実行できてしまうのかがわかりません。indexはどこから呼び出されているのでしょうか。

どこから呼び出されるかというのはデコレータとは別の問題です。デコレータがテーブルに登録し、イベントループが、イベントに応じてテーブルに登録されたエントリを呼び出します。

投稿2021/09/08 12:10

ppaul

総合スコア24666

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問