装饰器是一种特殊的函数,它可以在装饰其他函数或者类,使用装饰器可以在函数执行之前和执行之后添加相应的操作,另外装饰器至少有两层,装饰器可以装饰有N个参数的函数,装饰器可以返回函数值
先看如下代码:
#!/usr/bin/env python# -*- coding:utf-8 -*- ① def f1(arg):④ arg()⑤ ② def func(): ⑥ print '123'⑦ ③ f1(func)
分析以上代码在解释器中的执行顺序:
代码开始运行时顺序是从上到下依次执行,第一、二步先将函数f1、func解释加载到内存中,其中函数中的内容arg()、print‘123’不执行,第三步(③)执行f1(func),当执行f1(func)时,就回到开始处,即第四步(④),找到函数f1,然后传入参数arg,其实的参数是函数func,所以将func函数载入到函数f1内,接着进入第五步,arg(),其实就是执行func()函数,即再回func函数,也就是第六步,而func函数的内部代码的动作是打印123,所以就执行打印操作,即执行第七步print‘123’
所以再接着看如下代码:
def auth(func):
def inner():
func()#原函数
return inner
假设函数f1为最原始的函数
def f1():
print 'f1'
所以auth认证函数调用f1过程实质如下
def auth(func):#func = f1 ===>func()<===>f1()
def inner():
print 'before'
func)#====>原函数
return inner
对auth(f1)进行变量赋值
ret = auth(f1) =def inner():
print 'before'
func() #f1 ==>原函数
将ret替换成f1
f1 = auth(f1) =def iner():
print 'before'
f1()
从上述整个过程来看,auth函数对函数f1的封装然后再把封装后的函数重新赋值到f1,即对被封装的函数名重新赋值
由于Python给我们提供了更便捷的方式,即@装饰器名字即可,所以以上的部分可以归纳成如下:
def auth(func):#func = f1 ===>func()<===>f1()
def inner():
print 'before'
func)#====>原函数
return inner
@auth #===============================>此处就是调用装饰器
def f1():
print 'f1'
对于装饰器记住如下两点:
(装饰器的功能简单的说就是函数加上Python的一个语法堂)
①装饰器是一个函数,执行装饰器函数
②装饰器对被装饰的函数名进行重新赋值
装饰器各种情况举例说明(以下各列中basic.py为基础代码部分,index.py为业务线程序代码,业务线代码从基础代码中获取数据):
⒈不带参数的装饰器
[root@Python day003]#vim basic.py
#!/usr/bin/env python#-*- coding:utf-8 -*-def auth(arg): def inner(): print "before" arg() print "after" return inner def f1(): print "这是测试函数f1"def f2(): print "这是测试函数f2"def f3(): print "这是测试函数f3"def f4(): print "这是测试函数f4"
[root@Python day003]vim index.py
#!/usr/bin/env python#-*- coding:utf-8 -*-import basicbasic.f1()
运行index.py输出如下结果:
[root@Python day003]#python index.py这是测试函数f1
修改basic.py代码为如下:
[root@Python day003]#vim basic.py
#!/usr/bin/env python#-*- coding:utf-8 -*-def auth(func): def inner(): print "before" arg() print "after" return inner@authdef f1(): print "这是测试函数f1"def f2(): print "这是测试函数f2"def f3(): print "这是测试函数f3"@authdef f4(): print "这是测试函数f4"@authdef f5(): print "这是测试函数f5"
[root@Python day003]#vim index.py
#!/usr/bin/env python#-*- coding:utf-8 -*-import basicbasic.f1()
运行index.py查看结果:
[root@Python day003]#python index.py
before 这是测试函数f1 after
⒉带固定参数的装饰器(arg)
basic脚本代码:
#!/usr/bin/env python# -*- coding:utf-8 -*-__author__ = 'ryan'def auth(func): def inner(): print "before" func() print "after" return innerdef auth_arg(func): def inner(arg): print "before" func(arg) print "after" return inner@authdef f1(): print "这是测试函数F1"def f2(): print "这是测试函数F2"def f3(): print "这是测试函数F3"def f4(): print "这是测试函数F4"@auth_argdef f5(arg): print "这是测试函数F5",arg """@authdef f1(): print "这是测试函数F1" ==============>相当于在执行函数auth(func),func函数体内的返回函数inner(),在返回inner函数时,先回到inner函数,进行执行inner函数体内的语句,即print "before" 、print "before"、func()等语句。其中func()就是原函数f1,而auth在整个函数都执行完之后就会返回一个值,然后将该值重新赋给原函数名f1,此时的函数f1功能已经发生变化,比之前未被auth调用之后多了输出"before"和"after"功能"""
注:装饰器内层函数inner中的参数arg可以是n、m、x等字母、单词,不固定为某个字母或者单词,只是用它们做为一个标识符号,可以和原来函数f5(arg)中的参数相同,也可以不相同
index脚本代码:
#!/usr/bin/env python# -*- coding:utf-8 -*-__author__ = 'ryan'import basicbasic.f1()print "=========这里是分割线========"basic.f5('1.1.1.1')
运行index.py脚本结果如下:
before这是测试函数F1after=========这里是分割线========before这是测试函数F5 1.1.1.1after
3、带动态参数的装饰器(*arg,**kwargs)
basic.py代码如下:(修改的部分为内层函数inner带上参数*args、**kwargs)
#!/usr/bin/env python# -*- coding:utf-8 -*-__author__ = 'ryan'def auth(func): def inner(*args,**kwargs): print "before" func(*args,**kwargs) print "after" return inner@authdef f1(): print "这是测试函数F1"def f2(): print "这是测试函数F2"def f3(): print "这是测试函数F3"def f4(): print "这是测试函数F4"@authdef f5(arg): print "这是测试函数F5",arg
index.py代码如下:(内容不作改动)
#!/usr/bin/env python# -*- coding:utf-8 -*-__author__ = 'ryan'import basicbasic.f1()print "=========这里是分割线========"basic.f5('1.1.1.1')
运行(index.py)结果如下:
before这是测试函数F1after=========这里是分割线========before这是测试函数F5 1.1.1.1after
4、含返回值的装饰器
basic.py脚本示例代码:
#!/usr/bin/env python# -*- coding:utf-8 -*-__author__ = 'ryan'def auth(func): def inner(*args,**kwargs): print "before" func(*args,**kwargs)#这里的func函数就是原函数,这里就是fetch_server_list print "after" return inner#@auth #===============================>不加装饰器时def fetch_server_list(arg): server_list=['host1','host2','host3'] return server_list
index.py代码如下:
#!/usr/bin/env python# -*- coding:utf-8 -*-__author__ = 'ryan'import basicret_list =basic.fetch_server_list('host')print ret_list
运行结果:
['host1', 'host2', 'host3']
发现拿到了主机名列表
下面添加装饰器,即将代码#@auth前的“#”注释去掉:
#!/usr/bin/env python# -*- coding:utf-8 -*-__author__ = 'ryan'def auth(func): def inner(*args,**kwargs): print "before" func(*args,**kwargs)#这里的func函数就是原函数,这里就是fetch_server_list print "after" return inner@auth #===============================>增加装饰器时def fetch_server_list(arg): server_list=['host1','host2','host3'] return server_list
index.py代码如下:
#!/usr/bin/env python# -*- coding:utf-8 -*-__author__ = 'ryan'import basicret_list =basic.fetch_server_list('host')print ret_list
运行结果:
beforeafterNone
解释:执行index.py时,其实就是间接执行basic.py中的函数fetch_server_list(arg),但是因为fetch_server_list(arg)加载了装饰器,所以此时的fetch_server_list函数其实就是装饰器中的函数inner(*args,**kwargs),所以就执行inner(*,**kwargs)函数,即执行:
def inner(*args,**kwargs): print "before" func(*args,**kwargs)#原fetch_server_list(arg)函数 print"after"
此时inner函数体中的func(*args,**kwargs)实质是没有加载装饰器之前的(原fetch_server_list函数)fetch_server_list(arg)函数,这里要与inner(*args,**kwargs)函数是fetch_server_list(arg)函数区分开来,即:一个是加载装饰器之后的(即新的fetch_server_list),一个是加载装饰器之前的(即原fetch_server_list).所以再回过来看输出结果,其中有一个None,None代表函数没有返回值,即函数没有返回值时候,默认返回None,没有返回值的函数如果将其赋值给某个变量,然后输出变量,得到的值就显示None,从inner函数体可以发现该函数是没有返回值的(即没有return 语句),
def inner(*args,**kwargs): print "before" func(*args,**kwargs)#这里的func函数就是原函数fetch_server_list print "after"
但是原函数体内有返回值(即return 语句),即有返回主机列表
def fetch_server_list(arg): server_list=['host1','host2','host3'] return server_list
这里将装饰器再做修改,代码如下:
basic.py代码:
#!/usr/bin/env python# -*- coding:utf-8 -*-__author__ = 'ryan'def auth(func): def inner(*args,**kwargs): print "before" temp = func(*args,**kwargs)#这里的func函数代表的就是原函数fetch_server_list print "after" return temp return inner@authdef fetch_server_list(arg): server_list=['host1','host2','host3'] return server_list
index.py代码:
#!/usr/bin/env python# -*- coding:utf-8 -*-__author__ = 'ryan'import basicret =basic.fetch_server_list('host')print ret
运行结果:
beforeafter['host1', 'host2', 'host3']
5、实现登陆验证原理
增加登录验证函数login,判断用户登录用户是否合理,如果合理就执行后面获取主机列表函数,否则就直接输出"invalid user"
basic.py代码如下:
#!/usr/bin/env python# -*- coding:utf-8 -*-__author__ = 'ryan'#登录验证函数def login(): name = 'ceshi' if name == 'user01': return True else: return False#装饰器def auth(func): def inner(*args,**kwargs): is_login = login() if not is_login: return 'invalid user' temp = func(*args,**kwargs)#这里的func函数就是原函数,这里就是fetch_server_list print "after" return temp return inner@auth#业务线函数def fetch_server_list(arg): server_list=['host1','host2','host3'] return server_list
index.py代码如下:
#!/usr/bin/env python# -*- coding:utf-8 -*-__author__ = 'ryan'import basicret =basic.fetch_server_list('host')print ret
运行idnex.py输出如下结果:
invalid user
如果将登录验证函数中的if name == 'ceshi',再看结果:
after['host1', 'host2', 'host3']
6、实现token验证
token验证的机制是业务线(index.py代码为业务线程序)拿着token到基础代码中去验证,如果跟基础代码中的已存在的token(这里指basic.py中的local_key)相等,则验证通过,否则就失败
basic.py代码如下:
#!/usr/bin/env python# -*- coding:utf-8 -*-__author__ = 'ryan'def login(key): local_key = 'dagfageageqgeag224!@#*(&dfag' if local_key == key: return True else: return Falsedef auth(func): def inner(*args,**kwargs): #key = kwargs["token"] #del kwargs["token"] key = kwargs.pop('token') is_login = login(key) if not is_login: return 'invalid user' temp = func(*args,**kwargs)#这里的func函数就是原函数fetch_server_list(arg) print "after" return temp return inner@authdef fetch_server_list(arg): server_list=['host1','host2','host3'] return server_list
解释:上述代码中,因为inner函数传入两个参数,
def inner(*args,**kwargs):
而实际函数fetch_server_list则只有一个参数,
def fetch_server_list(arg):
所以先通过key=kwargs["token"]获取token的值赋给key,然后用del命令删除kwargs字典中的kwargs["token"]的键值对,即:
#key = kwargs["token"]#del kwargs["token"]
当然,在字典中可以用kwargs.pop("token")语法来删除token键值对,所以上述的key=kwargs['token']
和del kwargs['token']两句与kwargs['token']是等价的
index.py代码如下:
#!/usr/bin/env python# -*- coding:utf-8 -*-__author__ = 'ryan'import basickey = 'dagfageageqgeag224!@#*(&dfag'ret =basic.fetch_server_list('host',token=key)print ret
运行index.py输出如下结果:
after['host1', 'host2', 'host3']
7、多装饰器装饰同一个函数
多装饰器可以理解为多个盒子,一层一层的包装函数,具体效果如下
一个装饰器情况:(demo.py代码如下)
#!/usr/bin/env python# -*- coding:utf-8 -*-__author__ = 'ryan'def f1(func): def inner(): print "第一个装饰器开始" func() print "第一个装饰器结束" return innerdef f2(func): def inner(): print "第二个装饰器开始" func() print "第二个装饰器结束" return inner@f1def foo(): print "foo是一个测试函数"foo()
运行结果如下:
第一个装饰器开始foo是一个测试函数第一个装饰器结束
两个装饰器情况(demo.py代码如下):
#!/usr/bin/env python# -*- coding:utf-8 -*-__author__ = 'ryan'def f1(func): def inner(): print "第一个装饰器开始" func() print "第一个装饰器结束" return innerdef f2(func): def inner(): print "第二个装饰器开始" func() print "第二个装饰器结束" return inner@f2@f1def foo(): print "foo是一个测试函数"foo()
输出结果:
第二个装饰器开始第一个装饰器开始foo是一个测试函数第一个装饰器结束第二个装饰器结束
从以上输出结果可以发现,多个装饰器装饰同一个函数的时候,是一层一层的对函数进行包装,最外层的是最外面的装饰器,最里面的是靠近函数最近的装饰器,简单的说多装饰器就是盒子模型。
8、装饰器加参数
上面几种情况下都是装饰器不带参数的,下面的例子介绍装饰器带参数情况
def Filter(a1,a2): def outer(main_func): def wrapper(request,kargs): print a1 main_result = main_func(request,kargs) print a2 return main_result return wrapper return outer @Filter(f5,f6)def Index(request,kargs): print 'index' #引用函数Index('python','test')
执行过程:
执行过程:
解释器先执行def Filter(a1,a2),第二步执行@Filter(f5,f6),会暂时先忽略@符号,执行Filter函数体的的内容,即回到上面def Filter(a1,a2)函数体将函数Filter(a1,a2)放进内存,但是此时Filter内的def outer函数体不执行,而是直接执行return outer,即返回outer
def Filter(a1,a2): def outer(main_func): def wrapper(request,kargs): print a1 main_result = main_func(request,kargs) print a2 return main_result return wrapper return outer
此时执行到@Filter(f5,f6),但是这个时候的@Filter即变成了@outer,即如下所示:
@outer(f5,f6)def Index(request,kargs): print 'index'
接下来执行函数outer,即:
def outer(main_func): def wrapper(request,kargs): print a1 main_result = main_func(request,kargs) print a2 return main_result return wrapper
同样,此时outer里的函数wrapper也是不会被执行,只是运行return wrapper语句进行返回值,接下来就开始执行wrapper函数,而此时的wrapper函数即为装饰后的Index函数,
对以上代码进行修改然后执行:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
__author__ = 'ryan'
def Filter(a1,a2): def outer(main_func): def wrapper(request,kargs): print a1 main_result = main_func(request,kargs) print a2 return main_result return wrapper return outer@Filter('参数1','参数2')def Index(request,kargs): print 'index', request,kargsIndex('python','装饰器')
输出结果:
参数1index python 装饰器参数2