Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
5
Help us understand the problem. What is going on with this article?
@ttsubo

Pythonのデコレータ動作メモ

More than 3 years have passed since last update.

デコレータを活用した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!]

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

5
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ttsubo
SDN分野に興味があります。 ただいま、golangを勉強中です。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
5
Help us understand the problem. What is going on with this article?