デコレータについてざっくりまとめ
タイトルの通り。
前回、高階関数についてざっくりまとめたので、その流れからデコレータもざっくりとまとめます。
デコレータとは?
最初はなんだか難しそうな感じがするなーと思っていましたが、紐解いてみると結構あっさりしてるものです。 端的に言ってしまえば、デコレータとは「処理をデコレーションして(別の処理で装飾して)置き換える記法」です。 高階関数を簡単に書くためのシンタックスシュガー・・と僕は考えています。 ポイントは「置き換える」ってところですかね。ある関数にデコレータを適用すると、その関数を呼ぶ時に必ずデコレーションされたもので呼び出されるようになります。
高階関数の書き方を振り返る
例によってこんな関数で考えてみます。
def outer(func): def inner(): print("before execute") func() print("after execute") return inner() def hello(): print ("Hello World")
hello関数は文字の出力、outer関数は引数の関数実行前後に文字列を差し込むやつですね。 これをセットで呼ぶ時は、こんな感じに書くよって話だったと思います。
outer(hello)
ただ、いちいちこの書き方をするのは面倒だと思います。 「もうhello関数はouter関数とセットで使うから一緒に呼べるようにしとこうよ・・・」とい風に。
実際にデコレートしてみる
こういう場合は、outer関数をデコレータとしてhello関数を置き換えてあげましょう。
def outer(func): def inner(): print("before execute") func() print("after execute") return inner() @outer def hello(): print ("Hello World")
hello関数の上に「@outer」をつけました。 これは「hello関数をouter関数でデコレートしましたよ」ってことです。 こうすることで、hello関数を実行するときは、常に
outer(hello)
を実行することど同義になります。
つまり、デコレータをつけることで
hello = outer(hello)
としている(置き換えている)んですね。 中身を見てしまえばそう身構える必要もないかなって感じです。
ただ、注意としては・・ このデコレータを適用したhello関数を実行すると、実態として実行されているのはouter関数の中にあるinner関数が実行されています。
print(hello.__name__)
>>> inner
「置き換えている」ということを忘れがちですが、もともと定義したhello関数とは別物だと考えてください
そこで、「いやいやhello関数を読んでるんだからそこはhelloになるはずでしょ」と思う気持ちもわかります。 そのモヤモヤを解決するためにはfunctors.wrapsを使ってください。
def outer(func): @functors.wraps def inner(): print("before execute") func() print("after execute") return inner()
実体となるinner関数に対してfunctors.wrapsをデコーレトしてあげることで、もとの関数の情報を保持しておくことができます。
print(hello.__name__)
>>> hello
あと、デコレータとして面倒なところは引数の受け渡しの部分がありますが・・・ 長くなるので別記事に。