まず関数が"オブジェクト"であることが分かっている必要があります。
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を引数付きで呼び出す方が先に実行されるのです。