1 什么是装饰器?
Python修饰器基于闭包原理,是赋予已存在的函数对象额外定制化功能的设计模式,使得代码简洁且易于维护,常用于插入日志、性能测试、权限校验等需求场景。例如某个具有登录功能的函数,现在希望添加数字签证核验功能,即可使用修饰器处理登录函数而无需变更登录函数的底层。Python语法糖@定义了修饰器
2 最通用的例子
def app(func): def warp(*args, **kwargs): print("正在执行功能1") result = func(*args, **kwargs) print("正在执行功能2") return result return warp @app def add(a, b): return a + b print(add(1,3)) # 正在执行功能1 # 正在执行功能2 # 4
*args
、**kwargs
用于接收被修饰函数的入口参数,*args、**kwargs分别用于将参数打包成tuple、dict给函数体调用。从上面可以看出,在不修改底层add函数的情况下,可以定制新功能。
3 实现日志打印修饰器
根据修饰器的原理,对上面的例子做小改动如下:
import logging def log(func): def warp(*args, **kwargs): try: result = func(*args, **kwargs) return result except Exception as e: logging.error(e) return warp
这里使用try-except
结构可以即使捕获异常信息输出到日志文件。
在某些应用场景,上面的结构还可以改进,比如Web服务可能要在输出日志后给前端反馈一个404代码,因此在增加一个定制化的返回内容err
def logger(err): def log(func): def warp(*args, **kwargs): try: result = func(*args, **kwargs) return result except Exception as e: logging.error(e) return err return warp return log
4 配置logging输出格式
import logging, os ''' * %(asctime)s 即日志记录时间,精确到毫秒 * %(levelname)s 即此条日志级别 * %(filename)s 即触发日志记录的python文件名 * %(funcName)s 即触发日志记录的函数名 * %(lineno)s 即触发日志记录代码的行号 * %(message)s 即这项调用中的参数 ''' if not os.path.exists('Log.log'): file = open('Log.log','w') logging.basicConfig( filename='Log.log', format="%(asctime)s - %(levelname)s - %(filename)s - %(funcName)s - %(lineno)s - %(message)s" )
5 测试
@logger('加法环节出错了!') def add(a, b): return a + b # 正常情况 print(add(1, 2)) # 输出3 # 异常情况 print(add(1, '2')) # 输出“加法环节出错了!” # Log.log中的记录 2021-10-22 21:34:54,736 - ERROR - test.py - warp - 51 - unsupported operand type(s) for +: 'int' and 'str'
于是只要把上面说的logger函数封装成单独一个文件,在其他位置import logger并用修饰器修饰你希望增加日志功能的函数即可,非常方便
6 附:完整代码
import logging, os ''' * %(asctime)s 即日志记录时间,精确到毫秒@breif: * %(levelname)s 即此条日志级别@param[in]: * %(filename)s 即触发日志记录的python文件名@retval: * %(funcName)s 即触发日志记录的函数名 * %(lineno)s 即触发日志记录代码的行号 * %(message)s 即这项调用中的参数 ''' if not os.path.exists('Log.log'): file = open('Log.log','w') logging.basicConfig( filename='Log.log', format="%(asctime)s - %(levelname)s - %(filename)s - %(funcName)s - %(lineno)s - %(message)s" ) ''' * @breif: 日志修饰器,为函数添加日志记录服务 * @param[in]: err -> 发生异常时返回的错误信息 * @retval: 加载日志服务的功能函数 ''' def logger(err): def log(func): def warp(*args, **kwargs): try: result = func(*args, **kwargs) return result except Exception as e: logging.error(e) return err return warp return log @logger('出错了') def add(a, b): return a+b print(add(1,'2'))