一个普通的装饰器写法:
def log(func):
def wrapper(*args, **kwargs):
print('被调用的函数'.format(func.__name__))
return func(*args, **kwargs)
return wrapper
此时有个疑问,为什么里面要嵌套wrapper函数?
原因:
- 如果没有嵌套,实际上装饰器返回的要么是原函数的定义,要么根本不是函数,也就是说函数根本没有被装饰。
- 即使碰巧得到了想要的结果也是装饰器在定义的阶段便运行的,这其实是不应该发生的,因为这意味着不调用函数,照样会有输出,而且效果不能在函数被调用时复现。
代码说明如下:
1、如果直接一个单层的装饰器
def log(func): #1
print('被调用的函数{}'.format(func.__name__)) #2
return func() #3
@log #4
def now(): #5
print('hello world !')
now()
此时#4运行时就会运行#2的print,并且装饰器要求#3 return一个函数,而不是函数的值(正确写法是return func
),因此#3的语法错误,会报错TypeError: 'NoneType' object is not callable
2、如果改正为返回func
def log(func): #1
print('被调用的函数{}'.format(func.__name__)) #2
return func #3
@log #4
def now(): #5
print('hello world !')
now()
这样写不会报错,但是会在@log时直接执行装饰器的内容,即执行print,此时装饰器返回的还是原函数,所以之后调用函数now就无法触发装饰器。也就是在没调用now的情况下我们也会得出一条装饰器的输出语句,因为这时候log函数执行了,所以需要两层嵌套的写法返回一个函数
python的设计者为什么这么设计
为什么不把单层的装饰器的执行顺序等价为现在的双层装饰器的执行顺序?
- 一是两层结构可以传递被装饰的函数now的参数
- 二是不改变函数now和函数log原来的结构,因为log本身就是一个函数,他与其他函数有相同的执行顺序
def log(func):
def wrapper(*args, **kwargs):
print('被调用的函数'.format(func.__name__))
return func(*args, **kwargs)
return wrapper
@log
def now(x):
print('hello {} !'.format(x))
now('world')
完全等价于now=log(now)
解释:
- 装饰器等于修改了原函数定义,返回的仍然是函数定义!而不是单纯的返回的函数运行结果
- 此时再调用被装饰函数实际执行的是已经被装饰过的函数(即可以理解为调用具有装饰器的函数,该函数的实际定义已经不是你看到的定义,而是被装饰器修改后的)
- 一次定义后即可重复使用,就像普通定义函数一般
- 个人以为如果用装饰器,又不嵌套的写法是完全有悖装饰器初衷的,这时大可不必用装饰器,还不如直接函数调用来的实在
装饰器是什么,有什么功能,能用在什么业务场景?
概念:
- 装饰器的实现是由闭包支撑的
- 装饰器本质上是⼀个python函数,它可以在让其他函数在不需要做任何代码的变动的前提下增加额外的功能
- 装饰器的返回值一个被装饰过的
函数定义
,也是⼀个函数的对象,它经常用于有切面需求的场景,实现路由传参,flask的路由传参依赖于装饰器,浏览器通过url访问到装饰器的路由,从而访问视图函数获得返回的HTML页面 - 装饰器发生在
定义
阶段而不是执行
阶段
应用场景:
- 可以在外层函数加上时间计算函数,计算函数运行时间;
- 计算函数运行次数;
- 可以用在框架的路由传参上;
- 插入日志,作为函数的运行日志;
- 事务处理,可以让函数实现事务的一致性,让函数要么一起运行成功,要么一起运行失败;
- 缓存,实现缓存处理;
- 权限的校验,在函数外层套上权限校验的代码,实现权限校验;