Python装饰器
Table of Contents
想理解Python的装饰器,首先要知道在Python中函数也是一个对象,所以可以:
- 将函数赋值给变量
- 将函数当做参数
- 返回一个函数
引子
如果想在login函数中输出调试信息,可以这样做:
测试结果:
缺点就是每次调用login都通过printdebug来调用
函数式调用
既然函数可以作为返回值,可以赋值给变量,那让代码优美一点:
这样每次只要调用debug_login就可以了。将原先的两个函数printdebug和login绑定到一起,成为debug_login。这种耦合叫内聚
语法糖
printdebug和login是通过
来结合的,但这似乎也是多余的,我们希望在定义login上加个标注,从而将printdebug和login结合起来。Python的解决方案是提供一个语法糖,用一个@符号来结合它们:
__decorator函数就是一个:使用函数作参数并且返回函数的函数。这样改进的好处是:
- 更简短的代码,将结合点放在函数定义时
- 不改变原函数的函数名
事实上在Python解释器发现login调用时,会将login转换为:
也就是说真正执行的是__decorator(把func绑定成实参login函数)这个函数
加上参数
login函数有参数
login函数可能有参数,比如login的时候传人user的信息。也就是说,要这样调用login:
Python会将login的参数直接传给__decorator这个函数。因此可以直接在__decorator中使用user变量:
测试结果:
事实上的调用过程是:
装饰器本身有参数
在定义decorator时,也可以带入参数,比如这样使用decorator,传入一个参数来指定debug level
测试结果:
此时的pringdebug函数相当于pringdebug_level(5)
装饰有返回值的函数
有时候login会有返回值,比如返回message来表明login是否成功:
这时候需要将返回值在decorator和调用函数间传递:
测试结果:
多个装饰器
可以对一个函数应用多个装饰器,这时需要留心的是应用装饰器的顺序对结果会产生影响。例如:
测试结果:
login和logout输出截然相同。造成这个输出不同的原因是应用装饰器的顺序不同。回头看看login的定义,是先应用others,然后才是printdebug。而logout函数正好相反,在逻辑上可以将logout函数应用装饰器的过程这样看:
灵活运用
装饰器不能对函数的一部分应用,只能作用于整个函数。假如想对下面这行语句应用装饰器:
那就需要对这行代码提取出一个函数,然后再对它应用修饰器:
测试结果:
实际上validate往往是个耗时的过程。为了提高应用的性能,会将validate的结果cache一段时间(30 seconds),借助decorator和上面的方法,可以这样实现:
测试结果: