差分
このページの2つのバージョン間の差分を表示します。
| 両方とも前のリビジョン 前のリビジョン 次のリビジョン | 前のリビジョン | ||
| python:decorator [2011/06/19 20:31] – [デコレートされた関数の実行] ともやん | python:decorator [2020/02/25 11:14] (現在) – [参考文献] ともやん | ||
|---|---|---|---|
| 行 8: | 行 8: | ||
| <code python> | <code python> | ||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||
| + | from functools import wraps # python 2.5 以降で利用可能 | ||
| # 関数情報を表示するデコレータ | # 関数情報を表示するデコレータ | ||
| def func_info(func): | def func_info(func): | ||
| # 引数 func をラップする関数を定義 | # 引数 func をラップする関数を定義 | ||
| - | def _func_wrapper(*args, **kwargs): | + | |
| + | | ||
| print '--- func_info ---' | print '--- func_info ---' | ||
| print ' | print ' | ||
| 行 23: | 行 25: | ||
| return result | return result | ||
| # 引数 func をラップした関数を返却 | # 引数 func をラップした関数を返却 | ||
| - | return | + | return |
| # デコレートされた関数を定義 | # デコレートされた関数を定義 | ||
| @func_info | @func_info | ||
| def add(param1, param2): | def add(param1, param2): | ||
| + | u""" | ||
| return param1 + param2 | return param1 + param2 | ||
| 行 53: | 行 56: | ||
| def func_info(func): | def func_info(func): | ||
| # 引数 func をラップする関数を定義 | # 引数 func をラップする関数を定義 | ||
| - | def _func_wrapper(*args, **kwargs): | + | def _func_info(*args, **kwargs): |
| print '--- func_info ---' | print '--- func_info ---' | ||
| print ' | print ' | ||
| 行 64: | 行 67: | ||
| return result | return result | ||
| # 引数 func をラップした関数を返却 | # 引数 func をラップした関数を返却 | ||
| - | return | + | return |
| </ | </ | ||
| - | 上記のデコレータでは、引数の func 関数をラップした | + | 上記のデコレータでは、引数の func 関数をラップした |
| ==== 関数のデコレート ==== | ==== 関数のデコレート ==== | ||
| - | デコレータ式で add 関数をデコレートすると、add 関数は @func_info デコレータの | + | デコレータ式で add 関数をデコレートすると、add 関数は @func_info デコレータの |
| <code python> | <code python> | ||
| @func_info | @func_info | ||
| def add(param1, param2): | def add(param1, param2): | ||
| + | u""" | ||
| return param1 + param2 | return param1 + param2 | ||
| 行 80: | 行 84: | ||
| 実行結果: | 実行結果: | ||
| < | < | ||
| - | < | + | < |
| </ | </ | ||
| デコレータ式は、デコレータの第一引数に関数を指定して、関数ラッパーを生成するコードと等価である。 | デコレータ式は、デコレータの第一引数に関数を指定して、関数ラッパーを生成するコードと等価である。 | ||
| 行 86: | 行 90: | ||
| @func_info | @func_info | ||
| def add(param1, param2): | def add(param1, param2): | ||
| + | u""" | ||
| return param1 + param2 | return param1 + param2 | ||
| </ | </ | ||
| 行 91: | 行 96: | ||
| <code python> | <code python> | ||
| def add(param1, param2): | def add(param1, param2): | ||
| + | u""" | ||
| return param1 + param2 | return param1 + param2 | ||
| add = func_info(add) | add = func_info(add) | ||
| 行 96: | 行 102: | ||
| ==== デコレートされた関数の実行 ==== | ==== デコレートされた関数の実行 ==== | ||
| - | デコレートされた関数を実行するということは、デコレータの | + | デコレートされた関数を実行するということは、デコレータの |
| <code python> | <code python> | ||
| @func_info | @func_info | ||
| def add(param1, param2): | def add(param1, param2): | ||
| + | u""" | ||
| return param1 + param2 | return param1 + param2 | ||
| 行 113: | 行 120: | ||
| 3 | 3 | ||
| </ | </ | ||
| - | デコレートされていない add1 関数の関数ラッパーを生成して、ラッパーに引数を渡して実行しても同じ結果が得られる。 | + | デコレータを関数として実行する場合、上記と等価なコードは以下のようになる。 |
| <code python> | <code python> | ||
| - | def add1(param1, param2): | + | # func_info(add) は _func_info を返すので |
| + | # 以下は _func_info(1, | ||
| + | print func_info(add)(1, | ||
| + | </ | ||
| + | ===== ドキュメンテーション文字列が失われないようにする(functools.wraps の使い方) ===== | ||
| + | デコレータによる関数ラップを行うと、関数のドキュメンテーション文字列などが失われてしまう。 | ||
| + | <code python> | ||
| + | # -*- coding: utf-8 -*- | ||
| + | |||
| + | # 関数情報を表示するデコレータ | ||
| + | def func_info(func): | ||
| + | # 引数 func をラップする関数を定義 | ||
| + | def _func_info(*args, | ||
| + | print '--- func_info ---' | ||
| + | print ' | ||
| + | print ' | ||
| + | # 引数 func を実行 | ||
| + | result = func(*args, **kwargs) | ||
| + | print ' | ||
| + | print ' | ||
| + | # 引数 func の実行結果を返却 | ||
| + | return result | ||
| + | # 引数 func をラップした関数を返却 | ||
| + | return _func_info | ||
| + | |||
| + | @func_info | ||
| + | def add(param1, param2): | ||
| + | u""" | ||
| return param1 + param2 | return param1 + param2 | ||
| - | # _func_wrapperを生成 | + | print '関数名:', add.__name__ |
| - | print func_info(add1) | + | print ' |
| - | # _func_wrapperに引数(1, 2)を与えて実行 | + | |
| - | print func_info(add1)(1, 2) | + | |
| </ | </ | ||
| 実行結果: | 実行結果: | ||
| < | < | ||
| - | <function _func_wrapper at 0xcff398> | + | 関数名: _wrapper_func |
| + | 説明: None | ||
| + | </code>\\ | ||
| + | この問題を解決するには、デコレータ内部の _func_info の定義に @wraps を追加する必要がある。 | ||
| + | <code python> | ||
| + | # -*- coding: utf-8 -*- | ||
| + | from functools import wraps # python 2.5 以降で利用可能 | ||
| + | |||
| + | # 関数情報を表示するデコレータ | ||
| + | def func_info(func): | ||
| + | # 引数 func をラップする関数を定義 | ||
| + | @wraps(func) | ||
| + | def _func_info(*args, | ||
| + | print '--- func_info ---' | ||
| + | print ' | ||
| + | print ' | ||
| + | # 引数 func を実行 | ||
| + | result = func(*args, **kwargs) | ||
| + | print ' | ||
| + | print ' | ||
| + | # 引数 func の実行結果を返却 | ||
| + | return result | ||
| + | # 引数 func をラップした関数を返却 | ||
| + | return _func_info | ||
| + | |||
| + | @func_info | ||
| + | def add(param1, param2): | ||
| + | u""" | ||
| + | return param1 + param2 | ||
| + | |||
| + | print ' | ||
| + | print ' | ||
| + | </ | ||
| + | 実行結果: | ||
| + | < | ||
| + | 関数名: add | ||
| + | 説明: パラメータ1、2を加算した値を返します。 | ||
| + | </ | ||
| + | |||
| + | ===== 引数を必要とするデコレータ ===== | ||
| + | デコレータが引数を必要とする場合は、関数を二重にラップする必要がある。(ここでは inc_doc 引数を追加してみる。) | ||
| + | <code python> | ||
| + | # -*- coding: utf-8 -*- | ||
| + | from functools import wraps # python 2.5 以降で利用可能 | ||
| + | |||
| + | # 関数情報を表示するデコレータ | ||
| + | def func_info(inc_doc): | ||
| + | def _func_info(func): | ||
| + | # 引数 func をラップする関数を定義 | ||
| + | @wraps(func) | ||
| + | def __func_info(*func_args, | ||
| + | print '--- func_info ---' | ||
| + | print ' | ||
| + | if inc_doc: | ||
| + | print ' | ||
| + | print ' | ||
| + | # 引数 func を実行 | ||
| + | result = func(*func_args, | ||
| + | print ' | ||
| + | print ' | ||
| + | # 引数 func の実行結果を返却 | ||
| + | return result | ||
| + | # 引数 func をラップした関数を返却 | ||
| + | return __func_info | ||
| + | # 引数 func をラップした関数を返却 | ||
| + | return _func_info | ||
| + | </ | ||
| + | デコレータの引数(inc_docにTrue)を指定してデコレートする。 | ||
| + | <code python> | ||
| + | # デコレートされた関数を定義 | ||
| + | @func_info(True) | ||
| + | def add(param1, param2): | ||
| + | u""" | ||
| + | return param1 + param2 | ||
| + | |||
| + | # デコレートされた関数を実行 | ||
| + | print ' | ||
| + | print add(1, 2) | ||
| + | </ | ||
| + | 実行結果: | ||
| + | < | ||
| + | 関数実行->: | ||
| --- func_info --- | --- func_info --- | ||
| - | func: < | + | func: < |
| + | doc: パラメータ1、2を加算した値を返します。 | ||
| args: (1, 2) kwargs: {} | args: (1, 2) kwargs: {} | ||
| result: 3 | result: 3 | ||
| 行 133: | 行 247: | ||
| 3 | 3 | ||
| </ | </ | ||
| + | デコレータを関数として実行する場合、上記と等価なコードは以下のようになる。 | ||
| + | <code python> | ||
| + | # func_info(True) は _func_info を返すので | ||
| + | # func_info(True)(add) は _func_info(add) と等価 | ||
| + | # _func_info(add) は __func_info を返すので | ||
| + | # 以下は __func_info(1, | ||
| + | print func_info(True)(add)(1, | ||
| + | </ | ||
| + | ===== 参考文献 ===== | ||
| + | |< | ||
| + | <a target=" | ||
| + | </ | ||
| + | <iframe style=" | ||
| + | </ | ||
| + | [[http:// | ||