LoginSignup
1
5

More than 5 years have passed since last update.

Pythonのデコレータ動作メモ

Last updated at Posted at 2017-09-01

デコレータを活用したPythonプログラミングを勉強していると、その場では、理解できても、すぐに、忘れてしまいますよね。
そこで、今回、動的にメソッドを追加するサンプルアプリを、実際に動作させてみて、各種パラメータが、どのような値を保持するかを、メモっておきました。

■ デコレータを活用して、動的にメソッドを追加してみる

(1) サンプルプログラム

引数をもつメソッド(func1, func2)をデコレートするサンプルプログラムになります。

sample1.py
import sys
import json
import logging

logging.basicConfig()
log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)


def api_method(api_name):
    def _api_method(fn):
        log.debug("fn={}".format(fn))
        log.debug("api_name={}".format(api_name))
        setattr(fn, '__api_name__', api_name)
        return fn
    return _api_method

class Sample(object):
    def __init__(self):
        log.debug(json.dumps(dir(self), sort_keys=False, indent=4))

        self._api_methods = {}
        for method_name in dir(self):
            method = getattr(self, method_name)
            try:
                name = getattr(method, '__api_name__')
                msg = 'API method {0} registered'.format(name)
                log.debug(msg)
                self._api_methods[name] = method
            except AttributeError:
                pass

        log.debug("_api_methods=[%s]"%self._api_methods)

    @api_method('func1')
    def _func1(self, params):
        print ("*** func1 : params=[%s]"%params)
        return "func1 is done!"

    @api_method('func2')
    def _func2(self, params):
        print ("*** func2 : params=[%s]"%params)
        return "func2 is done!"


if __name__ == '__main__':
    args = sys.argv
    if len(args) == 3:
        method = args[1]
        params = args[2]

    m = Sample()
    result = m._api_methods[method](params)
    print ("*** result=[%s]"%result)

(2) 実際に動かしてみる

まずは、func1メソッドを動かしてみる

$ python sample1.py func1 aaa
DEBUG:__main__:fn=<function Sample._func1 at 0x10dde60d0>
DEBUG:__main__:api_name=func1
DEBUG:__main__:fn=<function Sample._func2 at 0x10dde6158>
DEBUG:__main__:api_name=func2
DEBUG:__main__:[
    "__class__",
    "__delattr__",
    "__dict__",
    "__dir__",
    "__doc__",
    "__eq__",
    "__format__",
    "__ge__",
    "__getattribute__",
    "__gt__",
    "__hash__",
    "__init__",
    "__init_subclass__",
    "__le__",
    "__lt__",
    "__module__",
    "__ne__",
    "__new__",
    "__reduce__",
    "__reduce_ex__",
    "__repr__",
    "__setattr__",
    "__sizeof__",
    "__str__",
    "__subclasshook__",
    "__weakref__",
    "_func1",
    "_func2"
]
DEBUG:__main__:API method func1 registered
DEBUG:__main__:API method func2 registered
DEBUG:__main__:_api_methods=[{'func1': <bound method Sample._func1 of <__main__.Sample object at 0x10ddde898>>, 'func2': <bound method Sample._func2 of <__main__.Sample object at 0x10ddde898>>}]
*** func1 : params=[aaa]
*** result=[func1 is done!]

つづいて、func2メソッドを動かしてみる

$ python sample1.py func2 bbb
DEBUG:__main__:fn=<function Sample._func1 at 0x10f9790d0>
DEBUG:__main__:api_name=func1
DEBUG:__main__:fn=<function Sample._func2 at 0x10f979158>
DEBUG:__main__:api_name=func2
DEBUG:__main__:[
    "__class__",
    "__delattr__",
    "__dict__",
    "__dir__",
    "__doc__",
    "__eq__",
    "__format__",
    "__ge__",
    "__getattribute__",
    "__gt__",
    "__hash__",
    "__init__",
    "__init_subclass__",
    "__le__",
    "__lt__",
    "__module__",
    "__ne__",
    "__new__",
    "__reduce__",
    "__reduce_ex__",
    "__repr__",
    "__setattr__",
    "__sizeof__",
    "__str__",
    "__subclasshook__",
    "__weakref__",
    "_func1",
    "_func2"
]
DEBUG:__main__:API method func1 registered
DEBUG:__main__:API method func2 registered
DEBUG:__main__:_api_methods=[{'func1': <bound method Sample._func1 of <__main__.Sample object at 0x10f971908>>, 'func2': <bound method Sample._func2 of <__main__.Sample object at 0x10f971908>>}]
*** func2 : params=[bbb]
*** result=[func2 is done!]

正しく起動することができました。

■ デコレータを使用せず、動的にメソッドを追加してみる

(1) サンプルプログラム

デコレートを使用せずに、同じように動作するサンプルプログラムになります。

sample2.py
import sys
import json
import logging

logging.basicConfig()
log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)


class Sample(object):
    def __init__(self):
        log.debug(json.dumps(dir(self), sort_keys=False, indent=4))

    def func1(self, params):
        print ("*** func1 : params=[%s]"%params)
        return "func1 is done!"

    def func2(self, params):
        print ("*** func2 : params=[%s]"%params)
        return "func2 is done!"


if __name__ == '__main__':
    args = sys.argv
    if len(args) == 3:
        method = args[1]
        params = args[2]

    m = Sample()
    meth = getattr(m, method, None)
    if meth:
        result = meth(params)
        print ("*** result=[%s]"%result)

(2) 実際に動かしてみる

まずは、func1メソッドを動かしてみる

$ python sample2.py func1 aaa
DEBUG:__main__:[
    "__class__",
    "__delattr__",
    "__dict__",
    "__dir__",
    "__doc__",
    "__eq__",
    "__format__",
    "__ge__",
    "__getattribute__",
    "__gt__",
    "__hash__",
    "__init__",
    "__init_subclass__",
    "__le__",
    "__lt__",
    "__module__",
    "__ne__",
    "__new__",
    "__reduce__",
    "__reduce_ex__",
    "__repr__",
    "__setattr__",
    "__sizeof__",
    "__str__",
    "__subclasshook__",
    "__weakref__",
    "func1",
    "func2"
]
*** func1 : params=[aaa]
*** result=[func1 is done!]

つづいて、func2メソッドを動かしてみる

$ python sample2.py func2 bbb
DEBUG:__main__:[
    "__class__",
    "__delattr__",
    "__dict__",
    "__dir__",
    "__doc__",
    "__eq__",
    "__format__",
    "__ge__",
    "__getattribute__",
    "__gt__",
    "__hash__",
    "__init__",
    "__init_subclass__",
    "__le__",
    "__lt__",
    "__module__",
    "__ne__",
    "__new__",
    "__reduce__",
    "__reduce_ex__",
    "__repr__",
    "__setattr__",
    "__sizeof__",
    "__str__",
    "__subclasshook__",
    "__weakref__",
    "func1",
    "func2"
]
*** func2 : params=[bbb]
*** result=[func2 is done!]

こちらも、正しく起動することができました。

■さらに、拡張してみる

動的にメソッドを追加するアプリを作成する際に、デコレータを作成しない方が、シンプルにコードが書ける気がするけど、敢えて、デコレータを活用した方が望ましい場面は、どんな場合なんだろうか?
単純に、DEBUG文を追加するなどの、メソッドを装飾する目的だと考えるので、さらに拡張してみた。

(1) サンプルプログラム

sample3.py
import sys
import json
import logging

logging.basicConfig()
log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)


def api_method(api_name):
    def _api_method(fn):
        log.debug("fn={}".format(fn))
        log.debug("api_name={}".format(api_name))
        def _wrap(*args, **kwargs):
            log.debug("-- 前処理 -- [{}]".format(api_name))
            ret = fn(*args, **kwargs)
            log.debug("-- 後処理 -- [{}]".format(api_name))
            return ret
        return _wrap
    return _api_method

class Sample(object):
    def __init__(self):
        log.debug(json.dumps(dir(self), sort_keys=False, indent=4))

    @api_method('func1')
    def func1(self, params):
        print ("*** func1 : params=[%s]"%params)
        return "func1 is done!"

    @api_method('func2')
    def func2(self, params):
        print ("*** func2 : params=[%s]"%params)
        return "func2 is done!"


if __name__ == '__main__':
    args = sys.argv
    if len(args) == 3:
        method = args[1]
        params = args[2]

    m = Sample()
    meth = getattr(m, method, None)
    if meth:
        result = meth(params)
        print ("*** result=[%s]"%result)

(2) 実際に動かしてみる

まずは、func1メソッドを動かしてみる

$ python sample3.py func1 aaa
DEBUG:__main__:fn=<function Sample.func1 at 0x10aba10d0>
DEBUG:__main__:api_name=func1
DEBUG:__main__:fn=<function Sample.func2 at 0x10aba11e0>
DEBUG:__main__:api_name=func2
DEBUG:__main__:[
    "__class__",
    "__delattr__",
    "__dict__",
    "__dir__",
    "__doc__",
    "__eq__",
    "__format__",
    "__ge__",
    "__getattribute__",
    "__gt__",
    "__hash__",
    "__init__",
    "__init_subclass__",
    "__le__",
    "__lt__",
    "__module__",
    "__ne__",
    "__new__",
    "__reduce__",
    "__reduce_ex__",
    "__repr__",
    "__setattr__",
    "__sizeof__",
    "__str__",
    "__subclasshook__",
    "__weakref__",
    "func1",
    "func2"
]
DEBUG:__main__:-- 前処理 -- [func1]
*** func1 : params=[aaa]
DEBUG:__main__:-- 後処理 -- [func1]
*** result=[func1 is done!]

つづいて、func2メソッドを動かしてみる

$ python sample3.py func2 bbb
DEBUG:__main__:fn=<function Sample.func1 at 0x106a1d0d0>
DEBUG:__main__:api_name=func1
DEBUG:__main__:fn=<function Sample.func2 at 0x106a1d1e0>
DEBUG:__main__:api_name=func2
DEBUG:__main__:[
    "__class__",
    "__delattr__",
    "__dict__",
    "__dir__",
    "__doc__",
    "__eq__",
    "__format__",
    "__ge__",
    "__getattribute__",
    "__gt__",
    "__hash__",
    "__init__",
    "__init_subclass__",
    "__le__",
    "__lt__",
    "__module__",
    "__ne__",
    "__new__",
    "__reduce__",
    "__reduce_ex__",
    "__repr__",
    "__setattr__",
    "__sizeof__",
    "__str__",
    "__subclasshook__",
    "__weakref__",
    "func1",
    "func2"
]
DEBUG:__main__:-- 前処理 -- [func2]
*** func2 : params=[bbb]
DEBUG:__main__:-- 後処理 -- [func2]
*** result=[func2 is done!]

いい感じに動作するようになりましたね。
以上です

1
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
5