<Python> でこでこでこでこでこれーた。

デコレータ decorator

元の関数 function になんらかの変更、デコレートdecorateをして、元の関数と置き換えるものっぽい。

ちこっと試してみた。

まずは、ベースの関数作成&動作確認。

In [32]: def aaa(*args, **kwargs):
    ...:     '''return args/kwargs'''
    ...:     return '{} {}'.format(args, kwargs)
    ...: 
    ...: 

In [33]: aaa.__name__
Out[33]: 'aaa'

In [34]: aaa.__doc__
Out[34]: 'return args/kwargs'

In [35]: aaa(1, 2, key=1)
Out[35]: "(1, 2) {'key': 1}"

で、デコレーター関数作成。
関数funcを引数にとって、
で、ラッパー関数wrapperを定義して、そのラッパー関数を返すreturnする。
元の関数funcの引数*args, **kwargsは、ラッパー関数wrapperに渡す。

In [36]: def deco(func):
    ...:     '''add tag'''
    ...:     def _wrapper(*args, **kwargs):
    ...:         return '<tag>' + func(*args, **kwargs) + '<tag>'
    ...:     return _wrapper
    ...: 
    ...: 

In [37]: deco.__name__
Out[37]: 'deco'

In [38]: deco.__doc__
Out[38]: 'add tag'

で、デコる、動作確認。

In [39]: @deco
    ...: def aaa(*args, **kwargs):
    ...:     '''return args/kwargs'''
    ...:     return '{} {}'.format(args, kwargs)
    ...: 
    ...: 

In [40]: aaa(1, 2, key=1)
Out[40]: "<tag>(1, 2) {'key': 1}<tag>"

In [41]: aaa.__name__
Out[41]: '_wrapper'

In [42]: aaa.__doc__

ここで、ミソは、デコレーターは、元の関数名を置き換えちゃうらしい。
なので、それを解決するには、functools.wrapsを使うとのこと。

In [43]: import functools

In [44]: def deco(func):
    ...:     '''add tag'''
    ...:     @functools.wraps(func)
    ...:     def _wrapper(*args, **kwargs):
    ...:         return '<tag>' + func(*args, **kwargs) + '<tag>'
    ...:     return _wrapper
    ...: 
    ...: 

In [45]: @deco
    ...: def aaa(*args, **kwargs):
    ...:     '''return args/kwargs'''
    ...:     return '{} {}'.format(args, kwargs)
    ...: 
    ...: 

In [46]: aaa(1, 2, key=1)
Out[46]: "<tag>(1, 2) {'key': 1}<tag>"

In [47]: aaa.__name__
Out[47]: 'aaa'

In [48]: aaa.__doc__
Out[48]: 'return args/kwargs' 

ふーん。なるほど。

あとは、さらに、デコ関数が引数を取る場合。
デコ関数のネストを一段深くする必要あるらしい。。。

In [55]: def deco(tag):
    ...:     def _deco(func):
    ...:         '''add tag'''
    ...:         def wrapper(*args, **kwargs):
    ...:             return '<' + tag +'>' + func(*args, **kwargs) + '<' + tag + '>'
    ...:         return wrapper
    ...:     return _deco
    ...: 
    ...: 

In [56]: deco
Out[56]: <function __main__.deco(tag)>

In [57]: @deco('bold')
    ...: def aaa(*args, **kwargs):
    ...:     '''return args/kwargs'''
    ...:     return '{} {}'.format(args, kwargs)
    ...: 
    ...: 

In [58]: aaa(1, 2, key=1)
Out[58]: "<bold>(1, 2) {'key': 1}<bold>"

参考先

qiita.com

qiita.com

blog.amedama.jp