目次
文書の過去の版を表示しています。
Python デコレータ
Python のデコレータは、関数やメソッドの機能を拡張することができる。
デコレータの基本形
デコレータは関数やメソッドをラップすることにより、ラップした処理の実行の有無やタイミングを制御することができる。そして、ラップした処理に対する引数や戻り値を取得して、処理の前後に独自の追加機能を実行することが可能となる。
以下に add 関数を @func_info デコレータでラップする例を示す。
# -*- coding: utf-8 -*- # 関数情報を表示するデコレータ def func_info(func): # 引数 func をラップする関数を定義 def _func_wrapper(*args, **kwargs): print '--- func_info ---' print 'func:', func print 'args:', args, 'kwargs:', kwargs # 引数 func を実行 result = func(*args, **kwargs) print 'result:', result print '-----------------' # 引数 func の実行結果を返却 return result # 引数 func をラップした関数を返却 return _func_wrapper # デコレートされた関数を定義 @func_info def add(param1, param2): return param1 + param2 # デコレートされた関数を実行 print '関数実行->: add(1, 2)' print add(1, 2)
実行結果:
関数実行->: add(1, 2) --- func_info --- func: <function add at 0x1644230> args: (1, 2) kwargs: {} result: 3 ----------------- 3
デコレータの動作原理
関数ラッパーの生成
デコレータとは関数ラッパーを生成する関数であるということが言える。これらはクロージャと呼ばれる仕組みにより実現される。
※クロージャとは定義された環境への参照を持った関数のこと。
# 関数情報を表示するデコレータ def func_info(func): # 引数 func をラップする関数を定義 def _func_wrapper(*args, **kwargs): print '--- func_info ---' print 'func:', func print 'args:', args, 'kwargs:', kwargs # 引数 func を実行 result = func(*args, **kwargs) print 'result:', result print '-----------------' # 引数 func の実行結果を返却 return result # 引数 func をラップした関数を返却 return _func_wrapper
上記のデコレータでは、引数の func 関数をラップした _func_wrapper 関数を返却している。
関数のデコレート
デコレータ式で add 関数をデコレートすると、add 関数は @func_info デコレータの _func_wrapper 関数ラッパーを返すようになる。
@func_info def add(param1, param2): return param1 + param2 # add はデコレータの関数ラッパーを返すようになる print add
実行結果:
<function _func_wrapper at 0x22f02a8>
デコレータ式は、デコレータの第一引数に関数を指定して、関数ラッパーを生成するコードと等価である。
@func_info def add(param1, param2): return param1 + param2
上記と以下のコードは等価である。
def add(param1, param2): return param1 + param2 add = func_info(add)
デコレートされた関数の実行
デコレートされた関数を実行するということは、デコレータの _func_wrapper 関数ラッパーに、ラップされる add 関数の引数を渡して実行しているのと同じことである。
@func_info def add(param1, param2): return param1 + param2 print add(1, 2)
実行結果:
--- func_info --- func: <function add at 0x22f0230> args: (1, 2) kwargs: {} result: 3 ----------------- 3
デコレートされていない add1 関数の関数ラッパーを生成して、ラッパーに引数を渡して実行しても同じ結果が得られる。
def add1(param1, param2): return param1 + param2 # _func_wrapperを生成 print func_info(add1) # _func_wrapperに引数(1, 2)を与えて実行 print func_info(add1)(1, 2)
実行結果:
<function _func_wrapper at 0xcff398> --- func_info --- func: <function add1 at 0xcff320> args: (1, 2) kwargs: {} result: 3 ----------------- 3