当一个被装饰的对象同时叠加多个装饰器时
装饰器的加载顺序是:由下而上
装饰器的执行顺序是:由上而下
加载装饰器就是将原函数名与装饰器内部的wrapper函数进行偷梁换柱
执行装饰器实际上就是执行装饰器内部的wrapper函数。
我们来看下面这段代码
我们定义了两个装饰器:无参装饰器timmer与有参装饰器auth
我们用这两个装饰器去修饰index函数。让我们看看到底是发生了一件什么事
import time
def timmer(func):
def wrapper1(*args, **kwargs):
print('===================================>wrapper1运行了')
start = time.time()
res = func(*args, **kwargs)
stop = time.time()
print('run time is %s' % (stop - start))
return res
return wrapper1
def auth(engine='file'):
def xxx(func):
def wrapper2(*args, **kwargs):
print('===================================>wrapper2运行了')
name = input('username>>>: ').strip()
pwd = input('password>>>: ').strip()
if engine == 'file':
print('基于文件的认证')
if name == 'egon' and pwd == '123':
print('login successfull')
res = func(*args, **kwargs)
return res
elif engine == 'mysql':
print('基于mysql的认证')
elif engine == 'ldap':
print('基于ldap的认证')
else:
print('错误的认证源')
return wrapper2
return xxx
@timmer
@auth(engine='file')
def index():
print('welcome to index page')
time.sleep(2)
index()
文章一开头,我们就已经说了,装饰器的加载顺序是由下而上的。就是说离被装饰函数最近的装饰器先加载。
那么在这段代码中可以看到,是@auth(engine='file')先被加载,在加载的时候,python一遇到函数名+()就会触发函数的执行,也就是说,加载的第一步就是执行了auth(engine='file')函数。
我们拐过头来去看auth函数的代码,执行auth函数其实就干了两件事,定义一个叫xxx的函数,然后将xxx函数内存地址作为返回值返回。没有调用xxx就不会触发xxx函数的执行。
auth(engine='file')函数执行完毕以后,语法糖:'@'符号开始生效,它的作用就是将正下方的函数名(函数内存地址)作为参数传入装饰器并将原函数名作为变量名去接收。
在这里就是干了这样一件事:index=xxx(index)(这里需要注意。第一:为什么是xxx呢?因为在auth函数执行完毕后把xxx函数的内存地址作为返回值返回了。所以这里是xxx。第二:括号内的index和等号左边的index不是同一个东西,括号内的index是原函数index的内存地址,等号左边的index就是一个变量名。)然后其实现在代码就是变成了下述代码的样子,只是这是在后台自动做的,我们看不到。这些都是Python解释器默默地帮我们实现的。
@timmer
index = xxx(index) # index = wrapper2函数内存地址
def index():
print('welcome to index page')
time.sleep(2)
我们看现在的代码,xxx函数加(),一看到函数名+()就会执行函数,返回上面代码去看,执行xxx函数干了两件事,干了两件事,定义了wrapper2函数,然后将wrapper2函数内存地址作为返回值返回。所以,变量名index是指向wrapper2函数的内存地址。
然后再向上加载timmer装饰器。函数名没有加(),则执行@语法。@的作用咱们上面说过了,忘掉的可以再看看。index这个变量名,指向的是wrapper2函数,将index函数名传入timmer函数,还是做了两件事,定义wrapper1函数,将wrapper1函数的内存地址作为返回值返回。这个时候已经变成了这个样子
index = wrapper1(index)
index = xxx(index) # index = wrapper2函数内存地址
def index():
print('welcome to index page')
time.sleep(2)
这个时候的第一行中,括号内的index指向的是wrapper2函数的内存地址,等号左边的index是一个新的变量名。这时的index指向的是wrapper1函数的内存地址。
以上就是装饰器加载时python解释器在背后默默帮我们做的事。
执行的时候会按照顺序依次执行,先执行wrapper1函数,再执行wrapper2函数,再执行被装饰函数。
这就是我们上面所说的: