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.info
def f():
    sleep_time = random.randint(0, 6) * 0.1
    time.sleep(sleep_time)

@ci2.info
def g():
    sleep_time = random.randint(0, 8) * 0.1
    time.sleep(sleep_time)

@ci3.info
def 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.log

f -> 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.log

g -> 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.log

h -> 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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章