python中最为重要的一个概念就是装饰器,这里记录一下原理和方法。从flask源码出发编写一个日志的装饰器。
1.1 装饰器的类型
装饰器实际是将函数作为参数传入到另一个函数中,并且可以执行传入的函数。这使得装饰器能够将很多统一流程或者是重复的内容集成在一起。
2.1 flask中的装饰器
flask的路由就是使用装饰器完成的,这里查看一下它的源码
""" 使用例子 @app.route('/') def index(): return 'Hello World' # 源码: """ 前面是整个Flask Object""" def route(self, rule, **options): def decorator(f): endpoint = options.pop('endpoint', None) # 将其注册到视图中 self.add_url_rule(rule, endpoint, f, **options) return f return decorator """
1.route部分: 其中route中rule是接收路由参数,因此可以知道,通过使用装饰器的语法糖@装饰名在里面传递参数,因此我们可以通过这个方法去记录日志的等级。 2.decorator部分: 将获取到的参数传递给add_url_rule添加规则,其中rule是路由,endpoint是默认的视图,f是编写传进来的函数,也就是自己编写路由完成的功能。因此使用闭包的形式将整个装饰器执行。
总结: 根据上述的源码可以知道使用闭包完成了整个装饰过程,并且装饰器执行的时候就会执行闭包的函数,因此在闭包中直接执行f()也是可以完成函数的执行。
2.2 实现一个日志管理
根据上述的总结按照以下的步骤就可以实现一个日志装饰器
1.编写日志的抽象函数 2.使用装饰器通过参数传递,确定日志类型 3.执行被装饰的函数,同时反序列化。
代码如下:
def log(path, **kwargs): """ :param path:日志的路径 :param kwargs: 其余的日志参数 :return: """ def decorator(f): statue = kwargs.pop("statue", "logging") # 默认的日志等级是logging # 输出代替反序列化 print("已存储函数%s,等级:%s" % (f.__name__, statue)) print("函数参数个数{0},函数返回值{1}".format(f.__code__.co_argcount, f.__defaults__)) print("函数参数参数名{0}".format(f.__code__.co_varnames)) print(f(10, 9)) return f return decorator @log("/one.txt") def one_record(a, b): """ 这个函数执行的内容 :param b: int :param a: int :return: sum """ return a + b @log("/two.txt", statue="danger") def two_record(a, b): """ :param a: int :param b: int :return: sub """ return abs((a - b))
结果
输出结果: 已存储函数one_record,等级:logging 函数参数个数2,函数返回值None 函数参数参数名('a', 'b') 19 已存储函数two_record,等级:danger 函数参数个数2,函数返回值None 函数参数参数名('a', 'b') 1
2.3 多个装饰器的使用
使用多个装饰器的时候,只需要继续在前面添加语法糖就可,实例如下:
# 新加一个带装饰函数 def path(**kwargs): def warp(f): print(f.__name__) return f return warp # 执行顺序是log-->path,相当于one_record=path(log(one_record)) @path() @log("/one.txt") def one_record(a, b): """ 这个函数执行的内容 :param b: int :param a: int :return: sum """ return a + b
2.4 装饰器的测试
为了检验结果,需要写一些测试用例证明装饰器的正确性,下面介绍一下编写的过程。
# 这就是正常调函数的结果 fun_res = one_record(1, 3) # 这个是装饰器调用的结果 # log是装饰的函数,one_record是被装饰的函数, (1, 3)表示函数中的值,因为log中的闭包需要一个函数类型,因此先将函数传递进去,再将参数传递进去。 dec_rest = log("12")(one_record)(1, 3) # 进行真假值的判断。 print(fun_res == dec_rest)
装饰器在python中是一个比较重要的内容,其核心思想就是利用python可以传递函数的特性,将函数作为参数进行传递,实现的关键部分是使用函数的闭包来完成装饰的过程。在实现测试的时候使用原函数和装饰函数的调用进行结果判断即可。