4533 发表于 2015-12-24 09:33:06

Python之装饰器

装饰器是一种特殊的函数,它可以在装饰其他函数或者类,使用装饰器可以在函数执行之前和执行之后添加相应的操作。看如下代码:

1
2
3
4
5
6
7
8
9
10
11
#!/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的一个语法堂)
①装饰器是一个函数,执行装饰器函数
②装饰器对被装饰的函数名进行重新赋值

对以上的代码进行具体优化:
⒈不带参数的装饰器
#vim basic.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/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"




vim index.py

1
2
3
4
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import basic
basic.f1()




运行index.py输出如下结果:

1
2
#python index.py
这是测试函数f1





修改basic.py代码为如下:

1
#vim basic.py





1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/env python
#-*- coding:utf-8 -*-
def auth(func):
   def inner():
          print "before"
          arg()
          print "after"
   return inner
@auth
def f1():
   print "这是测试函数f1"

def f2():
   print "这是测试函数f2"

def f3():
   print "这是测试函数f3"
@auth
def f4():
   print "这是测试函数f4"
@auth
def f5():
   print "这是测试函数f5"





1
#vim index.py





1
2
3
4
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import basic
basic.f1()




运行index.py查看结果:

1
#python index.py





1
2
3
before
这是测试函数f1
after





⒉带固定参数的装饰器(arg)
basic脚本代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/usr/bin/env python
# -*- coding:utf-8 -*-
__author__ = 'ryan'

def auth(func):
    def inner():
      print "before"
      func()
      print "after"
    return inner

def auth_arg(func):
    def inner(arg):
      print "before"
      func(arg)
      print "after"
    return inner

@auth
def f1():
    print "这是测试函数F1"

def f2():
    print "这是测试函数F2"

def f3():
    print "这是测试函数F3"

def f4():
    print "这是测试函数F4"

@auth_arg
def f5(arg):
    print "这是测试函数F5",arg
   
   
"""
@auth
def 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脚本代码:


1
2
3
4
5
6
7
#!/usr/bin/env python
# -*- coding:utf-8 -*-
__author__ = 'ryan'
import basic
basic.f1()
print "=========这里是分割线========"
basic.f5('1.1.1.1')




运行index.py脚本结果如下:


1
2
3
4
5
6
7
before
这是测试函数F1
after
=========这里是分割线========
before
这是测试函数F5 1.1.1.1
after





3、带动态参数的装饰器(*arg,**kwargs)
basic.py代码如下:(修改的部分为内层函数inner带上参数*args、**kwargs)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/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


@auth
def f1():
    print "这是测试函数F1"

def f2():
    print "这是测试函数F2"

def f3():
    print "这是测试函数F3"

def f4():
    print "这是测试函数F4"

@auth
def f5(arg):
    print "这是测试函数F5",arg




index.py代码如下:(内容不作改动)

1
2
3
4
5
6
7
#!/usr/bin/env python
# -*- coding:utf-8 -*-
__author__ = 'ryan'
import basic
basic.f1()
print "=========这里是分割线========"
basic.f5('1.1.1.1')




运行(index.py)结果如下:

1
2
3
4
5
6
7
before
这是测试函数F1
after
=========这里是分割线========
before
这是测试函数F5 1.1.1.1
after





4、含返回值的装饰器
basic.py脚本示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/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代码如下:

1
2
3
4
5
6
#!/usr/bin/env python
# -*- coding:utf-8 -*-
__author__ = 'ryan'
import basic
ret_list =basic.fetch_server_list('host')
print ret_list




运行结果:

1
['host1', 'host2', 'host3']




发现拿到了主机名列表


下面添加装饰器,即将代码#@auth前的“#”注释去掉:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/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代码如下:

1
2
3
4
5
6
#!/usr/bin/env python
# -*- coding:utf-8 -*-
__author__ = 'ryan'
import basic
ret_list =basic.fetch_server_list('host')
print ret_list




运行结果:

1
2
3
before
after
None




解释:执行index.py时,其实就是间接执行basic.py中的函数fetch_server_list(arg),但是因为fetch_server_list(arg)加载了装饰器,所以此时的fetch_server_list函数其实就是装饰器中的函数inner(*args,**kwargs),所以就执行inner(*,**kwargs)函数,即执行:


1
2
3
4
            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 语句),

1
2
3
4
def inner(*args,**kwargs):
      print "before"
      func(*args,**kwargs)#这里的func函数就是原函数fetch_server_list
      print "after"




但是原函数体内有返回值(即return 语句),即有返回主机列表

1
2
3
def fetch_server_list(arg):
    server_list=['host1','host2','host3']
    return server_list





这里将装饰器再做修改,代码如下:

basic.py代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/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"
      returntemp
    return inner
@auth
def fetch_server_list(arg):
    server_list=['host1','host2','host3']
    return server_list





index.py代码:

1
2
3
4
5
6
#!/usr/bin/env python
# -*- coding:utf-8 -*-
__author__ = 'ryan'
import basic
ret =basic.fetch_server_list('host')
print ret




运行结果:

1
2
3
before
after
['host1', 'host2', 'host3']



页: [1]
查看完整版本: Python之装饰器