48. 在类中定义装饰器

要求:

实现一个能将函数调用信息记录到日志的装饰器:

  1. 把每次函数的调用时间、执行时间、调用次数写入日志;
  2. 可以对被装饰函数分组,调用信息记录到不同日志;
  3. 动态修改参数,比如日志格式;
  4. 动态打开关闭日志输出功能。
@call_info(arg1, arg2, arg3,...)def func(a, b):
    ...

解决方案:

为了让装饰器在使用上更加灵活,可以把类的实例方法作为装饰器,此时在包裹函数中就可以持有实例对象,便于修改属性和拓展功能。


  • 对于内置函数locals()

更新并返回表示当前本地符号表的字典。在函数代码块但不是类代码块中调用locals()时将返回*变量。

请注意在模块层级上,locals()globals()是同一个字典。

>>> def f():
	x = 1
	y = [1, 2, 3]
	z = 'abc'
	print(locals())

	>>> f(){'x': 1, 'y': [1, 2, 3], 'z': 'abc'}

  • 对于logging模块:

这个模块为应用与库定义了实现灵活的事件日志系统的函数与类。

使用标准库提提供的logging API最主要的好处是,所有的Python模块都可能参与日志输出,包括你的日志消息和第三方模块的日志消息。

logging.getLogger函数:

logging.getLogger(name=None)

返回标准日志程序类,或者传递给setLoggerClass()的最后一个类。

logging.Logger类的addHandler方法:

logging.Logger.addHandler(hdlr: Handler)

处理程序实例将日志事件分派到特定的目的地。

logging有一个日志处理的主对象,其它处理方式都是通过addHandler添加进去的。logging的几种handle方式如下:

logging.StreamHandler:日志输出到流,可以是sys.stderr、sys.stdout或者文件;

logging.FileHandler:日志输出到文件。


  • 方案示例:
import time, logging, random

DEFAULT_FORMAT = '%(func_name)s -> %(call_time)s\t%(used_time)s\t%(call_n)s'class CallInfo:
    def __init__(self, log_path, format_=DEFAULT_FORMAT, on_off=True):
        self.log = logging.getLogger(log_path)
        self.log.addHandler(logging.FileHandler(log_path))
        self.log.setLevel(logging.INFO)             #日志级别
        self.format = format_
        self.is_on = on_off    #装饰器方法
    def info(self, func):
        _call_n = 0
        def wrap(*args, **kwargs):
            func_name = func.__name__
            call_time = time.strftime(('%x %X'), time.localtime())
            t0 = time.time()
            res = func(*args, **kwargs)
            used_time = time.time() - t0            nonlocal _call_n
            _call_n += 1
            call_n = _call_n            if self.is_on:
                self.log.info(self.format % locals())
            return res        return wrap        
    #动态修改日志格式
    def set_format(self, format_):
        self.format = format_        
    #动态开关日志输出
    def turn_on_off(self, on_off):
        self.is_on = on_off#测试代码ci1 = CallInfo('mylog1.log')ci2 = CallInfo('mylog2.log')ci3 = CallInfo('mylog3.log')@ci1.infodef f():
    sleep_time = random.randint(0, 6) * 0.1
    time.sleep(sleep_time)@ci2.infodef g():
    sleep_time = random.randint(0, 8) * 0.1
    time.sleep(sleep_time)@ci3.infodef h():
    sleep_time = random.randint(0, 7) * 0.1
    time.sleep(sleep_time)for _ in range(30):
    random.choice([f, g, h])()ci1.set_format('%(func_name)s -> %(call_time)s\t%(call_n)s')for _ in range(10):
    random.choice([f])()

结果:

# tail -f mylog1.logf -> 09/22/19 15:04:54	0.20096874237060547	1
f -> 09/22/19 15:04:54	0.10093975067138672	2
f -> 09/22/19 15:04:54	0.4008469581604004	3
f -> 09/22/19 15:04:56	0.5006699562072754	4
f -> 09/22/19 15:04:58	0.2009880542755127	5
f -> 09/22/19 15:04:58	0.5009510517120361	6
f -> 09/22/19 15:04:59	0.4005923271179199	7
f -> 09/22/19 15:04:59	0.5016672611236572	8
f -> 09/22/19 15:05:00	0.601273775100708	9
f -> 09/22/19 15:05:01	1.0967254638671875e-05	10
f -> 09/22/19 15:05:03	11
f -> 09/22/19 15:05:03	12
f -> 09/22/19 15:05:04	13
f -> 09/22/19 15:05:04	14
f -> 09/22/19 15:05:04	15
f -> 09/22/19 15:05:04	16
f -> 09/22/19 15:05:05	17
f -> 09/22/19 15:05:05	18
f -> 09/22/19 15:05:05	19
f -> 09/22/19 15:05:06	20# tail -f mylog2.logg -> 09/22/19 15:04:53	0.40066051483154297	1
g -> 09/22/19 15:04:53	0.8015897274017334	2
g -> 09/22/19 15:04:55	0.20100760459899902	3
g -> 09/22/19 15:04:55	0.5013411045074463	4
g -> 09/22/19 15:04:55	0.8017439842224121	5
g -> 09/22/19 15:04:57	0.6015071868896484	6
g -> 09/22/19 15:04:58	0.20061421394348145	7
g -> 09/22/19 15:04:58	0.10062575340270996	8
g -> 09/22/19 15:05:03	7.62939453125e-06	9# tail -f mylog3.logh -> 09/22/19 15:04:52	0.40088629722595215	1
h -> 09/22/19 15:04:52	0.7009398937225342	2
h -> 09/22/19 15:04:53	0.10054636001586914	3
h -> 09/22/19 15:04:54	0.10030746459960938	4
h -> 09/22/19 15:04:57	0.4019501209259033	5
h -> 09/22/19 15:05:00	0.6007544994354248	6
h -> 09/22/19 15:05:01	6.4373016357421875e-06	7
h -> 09/22/19 15:05:01	0.7011284828186035	8
h -> 09/22/19 15:05:02	0.7015573978424072	9
h -> 09/22/19 15:05:02	0.4015517234802246	10
h -> 09/22/19 15:05:03	0.3012206554412842	11


上一篇:48. 旋转图像


下一篇:debug cps 原因