デコレータとは(ざっくり)
これです
@
がついている子のことをデコレータといいます
デコレーション(装飾)してくれる子です
別名ラッパーと言ったり言わなかったりします
(今回,呼び名自体はなんだっていいので真偽は割愛です)
@handmade_decorator
def just_func(just_hensu):
print(just_hensu)
何ができるの?
関数の前後に他の処理を入れることができます
どう嬉しいの?
共通の処理を簡潔に適用できます
例えばこんな感じです
- どの関数も 5 回はリトライするようにしたい
- 関数実行後に共通したログを出力したい
- 関数実行前に共通のチェックを入れたい
このようなお悩みを,
関数定義の前に @
を書いてあげるだけで解決するのがデコレータです
今回デコレートする関数の紹介
引数を出力するだけのシンプルな関数です
# デコレートされる関数
def just_func(just_hensu):
print(just_hensu)
# 通常通り関数を呼び出す
just_func("通常通りの関数")
出力結果はこちらです
通常通りの関数
まずは @ を使わない方法
最初は @
を使いません
後で @
は出てくるので少々辛抱です
ということでまずはこちらをご覧ください
# デコレータ
def handmade_decorator(target_func, hensu):
print("前処理")
target_func(hensu)
print("後処理")
# デコレートされる関数
def just_func(just_hensu):
print(just_hensu)
# デコレーション
handmade_decorator(just_func, "デコレーション")
出力結果はこうです
「デコレーション」の前に「前処理」,
後に「後処理」を出力するようにしました
前処理
デコレーション
後処理
今回の例ではこうですね
このように,関数の前後に何かしらの処理をいれてくれる
それがデコレータです
関数を返すデコレータ
デコレータの中で関数を定義して,その関数を返すパターンです
ポイントは関数名を変えずに,前後に処理を追加できるところです
# デコレータ
def handmade_decorator(target_func):
"""関数を定義してその関数を返す"""
def wrap_func(hensu):
print("前処理")
target_func(hensu)
print("後処理")
return wrap_func
# デコレートされる関数
def just_func(just_hensu):
print(just_hensu)
# デコレートされた後
just_func = handmade_decorator(just_func)
just_func("デコレーション")
出力例は先ほどと同様です
変わったのは下記です
- デコレータの中で関数を定義してそれを返す
- デコレータから返ってきた関数を変数(
just_func
)に入れる - 返ってきた関数(
just_func
)を呼び出す
デコレータの中で関数を定義してそれを返す?
何を言ってるのかしら...ということでこちら解説です
デコレータから返ってきた関数を変数(just_func
)に入れる?
返ってきた関数(just_func
)を呼び出す?
何を言っちゃているの...?ということで解説です
ということで元々の just_func
関数を,
デコレータ handmade_decorator
によって装飾し,
変数 just_func
に返したわけです
ここ大事なのは,
元々の just_func
と変数 just_func
は同姓同名の違う子ということです
デコレートされる前後で id
と正体を確認してみます
丸っきり違う子だということがわかりますね
# デコレートされる前
print(id(just_func)) # 2121628872320
print(just_func) # <function just_func at 0x000001EDFAEF6E80>
# デコレートされた後
just_func = handmade_decorator(just_func)
print(id(just_func)) # 2121628867840
print(just_func) # <function handmade_decorator.<locals>.wrap_func at 0x000001EDFAEF5D00>
@を使ってデコレーション
お待たせしました
@
の登場です
この@
を使った書き方は糖衣構文といい,
かっこよく言うとシンタックスシュガーです
先ほどまでの書き方だと書くのが大変ということで,
書きやすくしたのが糖衣構文ってやつですね
飲みやすくするために甘いもので包んだお薬も糖衣っていいますよね
(某xx丸みたいな)
ということで見ていきましょう
"""シンタックスシュガー"""
# デコレータ
def handmade_decorator(target_func):
def wrap_func(hensu):
print("前処理")
target_func(hensu)
print("後処理")
return wrap_func
# デコレートされる関数
@handmade_decorator
def just_func(just_hensu):
print(just_hensu)
# デコレートされた関数を呼び出す
just_func("デコレーション")
出力結果は変わらないので割愛です
これまでと変わったのは以下です
-
just_func
の上に@handmade_decorator
がついている -
just_func = handmade_decorator(just_func)
が無くなっている
そしてポイントはまさにここです
# @をつけるだけでデコレーションできる
@handmade_decorator
def just_func(just_hensu):
print(just_hensu)
# 既にデコレーションされてるので後はそのまま呼び出すだけ
just_func("デコレーション")
デコレータに引数を渡したい
@handmade_decorator(arg)
みたいな感じで,
デコレータに引数を渡したい場合です
そのためには,デコレータを返す関数をつくります
下記の例だと create_decorator
がそれです
crete_decorator
で handmade_decorator
を定義して返し,
handmade_decorator
で wrap_func
を定義して返す
という流れです
"""デコレータに引数を渡したい"""
# デコレータを返す関数
def create_decorator(arg):
# デコレータ
def handmade_decorator(target_func):
def wrap_func(hensu):
print(arg) # 引数でもらってきた arg をここで出力
print("前処理")
target_func(hensu)
print("後処理")
return wrap_func
return handmade_decorator
# デコレートされる関数
# デコレータに arg をわたしてます
@create_decorator(arg="デコレータに渡す引数")
def just_func(just_hensu):
print(just_hensu)
# デコレートされた関数を呼び出す
just_func("デコレーション")
出力結果はこうです
デコレータに渡す引数
前処理
デコレーション
後処理
参考