装饰器类,通用于 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()
- 初始化时先将 目标函数储存到装饰器实例的私有属性 _func中,这时还没有进行装饰。
2 . call 方法的调用实际完场了装饰和执行函数两个部分。
get 方法是装饰通用类方法的关键
当装饰器装饰上 test_method时,此时的test_method指向的call_logger的实例对象。然后通过test_obj.test_method 时就触发了 get方法,
传入test_obj对象,赋给_obj属性,返回call_logger自身的对象。接着()直接触发call方法,装饰和执行目标方法。在_wrap_method中,注意inner函数第一个参数要传入self,因为装饰的是实例方法,实例方法的第一个参数为实例本身。通过偏函数来设置第一个默认参数为self._obj