python:decorator

差分

このページの2つのバージョン間の差分を表示します。

この比較画面へのリンク

両方とも前のリビジョン 前のリビジョン
次のリビジョン
前のリビジョン
python:decorator [2011/06/19 20:29] – [関数のデコレート] ともやん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):+    @wraps(func) 
 +    def _func_info(*args, **kwargs):
         print '--- func_info ---'         print '--- func_info ---'
         print 'func:', func         print 'func:', func
行 23: 行 25:
         return result         return result
     # 引数 func をラップした関数を返却     # 引数 func をラップした関数を返却
-    return _func_wrapper+    return _func_info
  
 # デコレートされた関数を定義 # デコレートされた関数を定義
 @func_info @func_info
 def add(param1, param2): def add(param1, param2):
 +    u"""パラメータ1、2を加算した値を返します。"""
     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 'func:', func         print 'func:', func
行 64: 行 67:
         return result         return result
     # 引数 func をラップした関数を返却     # 引数 func をラップした関数を返却
-    return _func_wrapper+    return _func_info
 </code> </code>
- 上記のデコレータでは、引数の func 関数をラップした _func_wrapper 関数を返却している。+ 上記のデコレータでは、引数の func 関数をラップした _func_info 関数を返却している。
  
 ==== 関数のデコレート ==== ==== 関数のデコレート ====
- デコレータ式で add 関数をデコレートすると、add 関数は @func_info デコレータの _func_wrapper 関数ラッパーを返すようになる。+ デコレータ式で add 関数をデコレートすると、add 関数は @func_info デコレータの _func_info 関数ラッパーを返すようになる。
 <code python> <code python>
 @func_info @func_info
 def add(param1, param2): def add(param1, param2):
 +    u"""パラメータ1、2を加算した値を返します。"""
     return param1 + param2     return param1 + param2
  
行 80: 行 84:
 実行結果: 実行結果:
 <code> <code>
-<function _func_wrapper at 0x22f02a8>+<function _func_info at 0x22f02a8>
 </code>\\ </code>\\
  デコレータ式は、デコレータの第一引数に関数を指定して、関数ラッパーを生成するコードと等価である。  デコレータ式は、デコレータの第一引数に関数を指定して、関数ラッパーを生成するコードと等価である。
行 86: 行 90:
 @func_info @func_info
 def add(param1, param2): def add(param1, param2):
 +    u"""パラメータ1、2を加算した値を返します。"""
     return param1 + param2     return param1 + param2
 </code> </code>
行 91: 行 96:
 <code python> <code python>
 def add(param1, param2): def add(param1, param2):
 +    u"""パラメータ1、2を加算した値を返します。"""
     return param1 + param2     return param1 + param2
 add = func_info(add) add = func_info(add)
行 96: 行 102:
  
 ==== デコレートされた関数の実行 ==== ==== デコレートされた関数の実行 ====
- デコレートされた関数を実行するということは、デコレータの関数ラッパー(_func_wrapper)に、ラップされる関数(add)の引数を渡して実行しているのと同じことである。+ デコレートされた関数を実行するということは、デコレータの _func_info 関数ラッパーに、ラップされる add 関数の引数を渡して実行しているのと同じことである。
 <code python> <code python>
 @func_info @func_info
 def add(param1, param2): def add(param1, param2):
 +    u"""パラメータ1、2を加算した値を返します。"""
     return param1 + param2     return param1 + param2
  
行 113: 行 120:
 3 3
 </code>\\ </code>\\
- デコレートされていない add1 関数の関数ラッパーを生成して、ラッパーに引数を渡して実行しても同じ結果が得られる。+ デコレータを関数して実行する場合上記と等価なコドは以下のようる。
 <code python> <code python>
-def add1(param1, param2):+# func_info(add) は _func_info を返すので 
 +# 以下は _func_info(1, 2) を実行しているのと等価 
 +print func_info(add)(1, 2) 
 +</code> 
 +===== ドキュメンテーション文字列が失われないようにする(functools.wraps の使い方) ===== 
 + デコレータによる関数ラップを行うと、関数のドキュメンテーション文字列などが失われてしまう。 
 +<code python> 
 +# -*- coding: utf-8 -*- 
 + 
 +# 関数情報を表示するデコレータ 
 +def func_info(func): 
 +    # 引数 func をラップする関数を定義 
 +    def _func_info(*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_info 
 + 
 +@func_info 
 +def add(param1, param2): 
 +    u"""パラメータ1、2を加算した値を返します。"""
     return param1 + param2     return param1 + param2
  
-# _func_wrapperを生成 +print '関名:'add.__name__ 
-print func_info(add1) +print '説明:'add.__doc__
-# _func_wrapperに引(12)を与えて実行 +
-print func_info(add1)(12)+
 </code>\\ </code>\\
 実行結果: 実行結果:
 <code> <code>
-<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, **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_info 
 + 
 +@func_info 
 +def add(param1, param2): 
 +    u"""パラメータ1、2を加算した値を返します。""" 
 +    return param1 + param2 
 + 
 +print '関数名:', add.__name__ 
 +print '説明:', add.__doc__ 
 +</code>\\ 
 +実行結果: 
 +<code> 
 +関数名: add 
 +説明: パラメータ1、2を加算した値を返します。 
 +</code> 
 + 
 +===== 引数を必要とするデコレータ ===== 
 + デコレータが引数を必要とする場合は、関数を二重にラップする必要がある。(ここでは 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, **func_kwargs): 
 +            print '--- func_info ---' 
 +            print 'func:', func 
 +            if inc_doc: 
 +                print 'doc:', func.__doc__ 
 +            print 'args:', func_args, 'kwargs:', func_kwargs 
 +            # 引数 func を実行 
 +            result = func(*func_args, **func_kwargs) 
 +            print 'result:', result 
 +            print '-----------------' 
 +            # 引数 func の実行結果を返却 
 +            return result 
 +        # 引数 func をラップした関数を返却 
 +        return __func_info 
 +    # 引数 func をラップした関数を返却 
 +    return _func_info 
 +</code> 
 + デコレータの引数(inc_docにTrue)を指定してデコレートする。 
 +<code python> 
 +# デコレートされた関数を定義 
 +@func_info(True) 
 +def add(param1, param2): 
 +    u"""パラメータ1、2を加算した値を返します。""" 
 +    return param1 + param2 
 + 
 +# デコレートされた関数を実行 
 +print '関数実行->: add(1, 2)' 
 +print add(1, 2) 
 +</code>\\ 
 +実行結果: 
 +<code> 
 +関数実行->: add(1, 2)
 --- func_info --- --- func_info ---
-func: <function add1 at 0xcff320>+func: <function add at 0x21ad500> 
 +doc: パラメータ1、2を加算した値を返します。
 args: (1, 2) kwargs: {} args: (1, 2) kwargs: {}
 result: 3 result: 3
行 133: 行 247:
 3 3
 </code> </code>
 + デコレータを関数として実行する場合、上記と等価なコードは以下のようになる。
 +<code python>
 +# func_info(True) は _func_info を返すので
 +# func_info(True)(add) は _func_info(add) と等価
 +# _func_info(add) は __func_info を返すので
 +# 以下は __func_info(1, 2) を実行しているのと等価
 +print func_info(True)(add)(1, 2)
 +</code>
 +===== 参考文献 =====
 +|<html>
 +<a target="_blank"  href="https://www.amazon.co.jp/gp/product/4048930613/ref=as_li_tl?ie=UTF8&camp=247&creative=1211&creativeASIN=4048930613&linkCode=as2&tag=tomoyan.net-22&linkId=76a5d8652e69ddf7b93828d61c52b0b5"><img border="0" src="//ws-fe.amazon-adsystem.com/widgets/q?_encoding=UTF8&MarketPlace=JP&ASIN=4048930613&ServiceVersion=20070822&ID=AsinImage&WS=1&Format=_SL250_&tag=tomoyan.net-22" ></a><img src="//ir-jp.amazon-adsystem.com/e/ir?t=tomoyan.net-22&l=am2&o=9&a=4048930613" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />
 +</html>|<html>
 +<iframe style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="https://rcm-fe.amazon-adsystem.com/e/cm?ref=tf_til&t=tomoyan.net-22&m=amazon&o=9&p=8&l=as1&IS2=1&detail=1&asins=4048930613&linkId=4991a49c3ce09cdb7570b40fe50a151c&bc1=ffffff&lt1=_top&fc1=333333&lc1=0066c0&bg1=ffffff&f=ifr"></iframe>
 +</html>|
 +[[http://www.python.jp/doc/2.6/library/functools.html|10.8. functools — 高階関数と呼び出し可能オブジェクトの操作 — Python v2.6.2 documentation]]
  • python/decorator.1308482961.txt.gz
  • 最終更新: 2019/05/18 02:23
  • (外部編集)