类装饰器,装饰函数或者类 方法

装饰器类,通用于 function 或 class method
参考: https://stackoverflow.com/questions/1288498/using-the-same-decorator-with-arguments-with-functions-and-methods

先贴上代码,后面会有一点解析:

import functools
import time
import logging

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)

class call_logger(object):
    """
    Decorator class to log calls on functions or class methods,
    e.g..
        @call_logger
        def my_function():
       class MyClass(object):         
            @call_logger
            def my_method(self):
    """
    def __init__(self, func):
        self._func = func 
        self._wrapped = None
        self._obj = None

    def __call__(self, *args, **kwags):
        if not self._wrapped:
            if self._obj:
                self._wrapped = self._wrap_method(self._func)
                self._wrapped = functools.partial(self._wrapped, self._obj)
            else:
                self._wrapped = self._wrap_function(self._func)
        return self._wrapped(*args, **kwags)

    def __get__(self, obj, type=None):
        self._obj = obj
        return self

    def _wrap_function(self, function):
        """
        Perform the decorator wrapping for a regular function.
        """
        @functools.wraps(function)
        def inner(*args, **kwags):
            """
            Implementation of the wraped function.
            """
            started = time.time()
            exc_name = None
            result = None
            try:
                result = function(*args, **kwags)
            except Exception as ex:
                exc_name = type(ex).__name__
                raise
            finally:
                _log_it(started, function, result, exc_name)
            return result
        return inner

    def _wrap_method(self, method):
        """
        Perform the decorator wrapping for a class method.
        """
        def inner(self, *args, **kwags):
            """
            Implementation of the wrapped function.
            """
            started = time.time()
            exc_name = None
            result = None
            try:
                result = method(self, *args, **kwags)
            except Exception as ex:
                exc_name = type(ex).__name__
                raise
            finally:
                _log_it(started, method, result, exc_name)
        return inner


def _log_it(started, method, result, exc_name):
    finished = time.time()
    elapsed = (finished - started) * 1000
    modname, methname = _get_caller_info(method)
    logging.debug("%s %s takes %s ms", modname, methname, elapsed)


def _get_caller_info(method):
    modname = method.__module__
    methname = method.__name__
    return modname, methname

@call_logger
def test():
    print 'test function'
    time.sleep(1)
    return True

class MyClass(object):

    @call_logger
    def test_method(self):
        time.sleep(1)
        print "test method"

if __name__ == "__main__":
    test()
    test_obj = MyClass()
    test_obj.test_method()
  1. 初始化时先将 目标函数储存到装饰器实例的私有属性 _func中,这时还没有进行装饰。

2 . call 方法的调用实际完场了装饰和执行函数两个部分。

  1. get 方法是装饰通用类方法的关键
    当装饰器装饰上 test_method时,此时的test_method指向的call_logger的实例对象。然后通过test_obj.test_method 时就触发了 get方法,
    传入test_obj对象,赋给_obj属性,返回call_logger自身的对象。接着()直接触发call方法,装饰和执行目标方法。

  2. 在_wrap_method中,注意inner函数第一个参数要传入self,因为装饰的是实例方法,实例方法的第一个参数为实例本身。通过偏函数来设置第一个默认参数为self._obj

github:
https://github.com/qq915522927/python_enhance/

你可能感兴趣的:(类装饰器,装饰函数或者类 方法)