装饰器是一种特殊的函数,它可以在装饰其他函数或者类,使用装饰器可以在函数执行之前和执行之后添加相应的操作,另外装饰器至少有两层,装饰器可以装饰有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