デコレーター 関数の修飾
簡単なfunctionを書きます。aとbを加える関数です。a + b = 30 答えは30です。
def add_num(a, b):
return a + b
r = add_num(10, 20)
print(r)
30
関数を実行する前に、startとendを出力します。
def add_num(a, b):
return a + b
print('start')
r = add_num(10, 20)
print('end')
print(r)
start end 30
関数がreturnされる前に、start,endがprintされました。
デコレーター
これを、デコレーターでより簡単にします。
何かしら、関数を実行する前と後とか、またはadd_num functionに機能を付け加えたいときにデコレーターを使います。
最初に、デコレーター関数print_infoを書きます。
引数には関数オブジェクト(ここではfunc)を書きます。
このfuncはインナーファンクションであるwrapper()の中で実行されreturnされます。
def print_info(func): def wrapper(*args, **kwargs): print('--start--') result = func(*args, **kwargs) print('--end--') return result return wrapper
関数print_infoの引数funcは関数オブジェクトで、func = add_numを意味します。
@print_info def add_num(a, b): return a +b r = add_num(10, 20) print(r)
#add_numを実行する際に、上のprint_infoに行き、innner関数のwrapperを呼び出します
wrapper内でfunc = add_numなので仮引数a,bをタプル*argsをアンラップした中に入れられ、print(r)により、実引数(10, 20)が渡され、10+20がreturnされます。
結果は、
--start-- --end-- 30
そこで、次のように関数sub_num()を追記しても、計算してくれます。
def print_info(func): def wrapper(*args, **kwargs): print('--start--') result = func(*args, **kwargs) print('--end--') return result return wrapper @print_info def add_num(a, b): return a + b r = add_num(10, 20) print(r) @print_info def sub_num(a, b): return a - b s = sub_num(100, 60) print(s)
--start-- --end-- 30 --start-- --end-- 40
一度デコレーターを書いておけば、@print_infoとするのみで、べつのsub_num()関数を簡単に追記できます。
順序に注意!
デコレーターが複数ある場合、デコレーターを@で呼び出す順序により結果が異なることに注意が必要です。
ここで、以下の様にするとどうなるか見てみます。
まず、
1)print_info()を書き、次にprint_more()を書きます。
次に、順序を逆にし
2)print_more()を書き、print_info()を書きます。
1)
def print_more(func): def wrapper(*args, **kwargs): print('func:', func.__name__) print('args:', args) print('kwargs:', kwargs) result = func(*args, **kwargs) print('result:', result) return result return wrapper def print_info(func): def wrapper(*args, **kwargs): print('--start--') result = func(*args, **kwargs) print('--end--') return result return wrapper @print_info @print_more def add_num(a, b): return a + b r = add_num(10, 20) print(r)
結果は
--start-- func: add_num args: (10, 20) kwargs: {} result: 30 --end-- 30
となります。次にここで
@print_infoと@print_moreの順序を逆にしてみると、結果が異なります。
2)
def print_more(func): def wrapper(*args, **kwargs): print('func:', func.__name__) print('args:', args) print('kwargs:', kwargs) result = func(*args, **kwargs) print('result:', result) return result return wrapper def print_info(func): def wrapper(*args, **kwargs): print('--start--') result = func(*args, **kwargs) print('--end--') return result return wrapper # @print_info @print_more @print_info def add_num(a, b): return a + b r = add_num(10, 20) print(r)
func: wrapper args: (10, 20) kwargs: {} --start-- --end-- result: 30 30
上記の様に、@print_infoと@print_moreの順序で結果が異なることに要注意です!
イメージとしては
@print_info
@print_more
の場合
add_numをprint_moreで包み込み、更にprint_infoで包み込むイメージで押さえておきます。
イメージ…print_info(print_more(add_num))
コメント