python装饰器

开闭原则:
在不修改原函数及其调用方式的情况下对原函数功能进行扩展
对代码的修改是封闭
不能修改被装饰的函数的源代码
不能修改被装饰的函数的调用方式

用函数的方式设想一下游戏里用枪的场景

 1 def game():
 2     print('压子弹')
 3     print('枪上膛')
 4     print('发射子弹')
 5 game()
 6 game()
 7 game()
 8 
 9 此时需要给枪增加一个瞄准镜,比如狙击远程目标时候需要加,狙击近程目标不用加
10 此时上边的代码就变成了现在的代码
11 
12 def sight():
13     print('专业狙击瞄准镜')
14     game()
15 sight()
16 sight()
17 sight()
18 此时的设计就不符合开闭原则(因为修改了原代码及调用名称)

装饰器(python里面的动态代理)
本质: 是一个闭包
组成: 函数+实参高阶函数+返回值高阶函数+嵌套函数+语法糖 = 装饰器
存在的意义: 在不破坏原有函数和原有函数调用的基础上,给函数添加新的功能

通用装饰器写法:

 1 def warpper(fn):        # fn是目标函数相当于func
 2     def inner(*args,**kwargs):      # 为目标函数的传参
 3         '''在执行目标函数之前操作'''
 4         ret = fn(*args,**kwargs)    # 调用目标函数,ret是目标函数的返回值
 5         '''在执行目标函数之后操作'''
 6         return ret      # 把目标函数返回值返回,保证函数正常的结束
 7     return inner
 8 
 9 #语法糖
10 @warpper    #相当于func = warpper(func)
11 def func():
12     pass
13 func()      #此时就是执行的inner函数

上边的场景用装饰器修改后

 1 方式一
 2 def game():
 3     print('压子弹')
 4     print('枪上膛')
 5     print('发射子弹')
 6 
 7 def sight(fn):      # fn接收的是一个函数
 8     def inner():
 9         print('安装专业狙击瞄准镜')
10         fn()        #调用传递进来的函数
11         print('跑路')
12     return inner    #返回函数地址
13 
14 game = sight(game)  #传递game函数到sight函数中
15 game()
16 
17 执行步骤
18 第一步定义两个函数game()为普通函数,sight()为装饰器函数
19 第二步定义game = sight(game)等于把game函数当做参数传递给sight(fn)装饰器函数fn形参
20 第三步执行sight(fn),fn在形参位置,相当于下边函数game()传参过来等于fn
21 第四步执行inner函数,然后return把inner函数内存地址当做返回值返回给sight(game)
22 第五步然后执行game(),相当于执行inner函数
23 第六步,执行inner函数,打印'狙击镜',执行fn()形参,由于fn形参等于game函数,所以执行game()函数,打印'压子弹','上膛','发射子弹'
24 第七步打印'跑路'
25 第八步把打印的结果返回给game()
26 
27 方式二
28 def sight(fn):      # fn接收的是一个函数
29     def inner():
30         print('安装专业狙击瞄准镜')
31         fn()        #调用传递进来的函数
32         print('跑路')
33     return inner    #返回函数地址
34 
35 @sight      #相当于game = sight(game)
36 def game():
37     print('压子弹')
38     print('枪上膛')
39     print('发射子弹')
40 game()
41 
42 执行步骤
43 第一步执行sight(fn)函数
44 第二步执行@sight,相当于把把game函数与sight装饰器做关联
45 第三步把game函数当做参数传递给sight(fn)装饰器函数fn形参
46 第四步执行inner函数,然后return把inner函数内存地址当做返回值返回给@sight
47 第五步执行game()相当相当于执行inner()函数,因为@sight相当于game = sight(game)
48 第六步打印'瞄准镜
49 第七步执行fn函数,因为fn等于game函数,所以会执行game()函数,打印'压子弹','上膛','发射子弹'.fn()函数执行完毕
50 第八步打印'跑路'
51 第九步然后把所有打印的结果返回给game()
52 
53 结果
54 安装专业狙击瞄准镜
55 压子弹
56 枪上膛
57 发射子弹
58 跑路

一个简单的装饰器实现

 1 事例1
 2 def warpper(fn):
 3     def inner():
 4         print('每次执行被装饰函数之前都要先经过这里')
 5         fn()
 6     return inner
 7 @warpper
 8 def func():
 9     print('执行了func函数')
10 func()
11 
12 结果
13 每次执行被装饰函数之前都要先经过这里
14 执行了func函数
15 
16 
17 事例2
18 def func(fn):
19     def inner():
20         print('check login')
21         fn()
22     return inner
23 
24 @func
25 def fss():
26     print('发说说')
27 
28 @func
29 def ftp():
30     print('发图片')
31 
32 binindex = 1
33 if binindex == 1:
34     fss()
35 else:
36     ftp()
37 结果
38 check login
39 发说说

 带有一个或多个参数的装饰器

 1 def sight(fn):                      #fn等于调用game函数
 2     def inner(*args,**kwargs):      #接受到的是元组("bob",123)
 3         print('开始游戏')
 4         fn(*args,**kwargs)    #接受到的所有参数,打散传递给user,pwd
 5         print('跑路')
 6     return inner
 7 @sight
 8 def game(user,pwd):
 9     print('登陆游戏用户名密码:',user,pwd)
10     print('压子弹')
11     print('枪上膛')
12     print('发射子弹')
13 game('bob','123')
14 结果
15 开始游戏
16 登陆游戏用户名密码: bob 123
17 压子弹
18 枪上膛
19 发射子弹
20 跑路
21 
22 
23 事例2
24 def zsq(func):
25     def inner(*args,**kwargs):
26         print('*'*30)
27         func(*args,**kwargs)
28     return inner
29 
30 @zsq
31 def pnum(num1,num2,num3):
32     print(num1,num2,num3)
33 
34 @zsq
35 def pnum2(num1):
36     print(num1)
37 
38 pnum(123,222,num3=666)
39 pnum2(9999)
40 结果
41 ******************************
42 123 222 666
43 ******************************
44 9999

动态传递一个或多个参数给装饰器

 1 def sight(fn):                      #调用game函数
 2     def inner(*args,**kwargs):      #接受到的是元组("bob",123)
 3         print('开始游戏')
 4         fn(*args,**kwargs)    #接受到的所有参数,打散传递给正常的参数
 5         print('跑路')
 6     return inner
 7 @sight
 8 def game(user,pwd):
 9     print('登陆游戏用户名密码:',user,pwd)
10     print('压子弹')
11     print('枪上膛')
12     print('发射子弹')
13     return '游戏展示完毕'
14 ret = game('bob','123')     #传递了两个参数给装饰器sight
15 print(ret)
16 
17 @sight
18 def car(qq):
19     print('登陆QQ号%s'%qq)
20     print('开始战车游戏')
21 ret2 = car(110110)          #传递了一个参数给装饰器sight
22 print(ret2)
23 结果
24 开始游戏
25 登陆游戏用户名密码: bob 123
26 压子弹
27 枪上膛
28 发射子弹
29 跑路
30 None
31 开始游戏
32 登陆QQ号110110
33 开始战车游戏
34 跑路
35 None
36 你会发现这两个函数执行的返回值都为None,但是我game定义返回值了return '游戏展示完毕',却没给返回

装饰器的返回值

 1 为什么我定义了返回值,但是返回值还是None呢,是因为我即使在game函数中定义了return '游戏展示完毕'
 2 但是装饰器里只有一个return inner定义返回值,但是这个返回值是返回的inner函数的内存地址的,并不是inner
 3 函数内部的return所以默认为None,所以应该定义一个inner函数内部的return返回值,而且也没有接收返回值的变量,
 4 所以要要设置ret = fn(*args,**kwargs)和return ret
 5 
 6 def sight(fn):                      #调用game函数
 7     def inner(*args,**kwargs):      #接受到的是元组("bob",123)
 8         print('开始游戏')
 9         ret = fn(*args,**kwargs)    #接受到的所有参数,打散传递给正常的参数
10         print('跑路')
11         return ret
12     return inner
13 @sight
14 def game(user,pwd):
15     print('登陆游戏用户名密码:',user,pwd)
16     print('压子弹')
17     print('枪上膛')
18     print('发射子弹')
19     return '游戏展示完毕'
20 ret = game('bob','123')     #传递了两个参数给装饰器sight
21 print(ret)
22 结果
23 开始游戏
24 登陆游戏用户名密码: bob 123
25 压子弹
26 枪上膛
27 发射子弹
28 跑路
29 游戏展示完毕
30 
31 
32 通过@装饰器(参数)的方式,调用这个函数,并传递参数,并把返回值再次当做装饰器进行使用
33 先计算@后的内容,把这个内容当做是装饰器
34 事例2
35 def wrapper_out(flag):      #装饰器本身的参数
36     def wrapper(fn):        #目标函数
37         def inner(*args,**kwargs):  #目标函数需要接受的参数
38             if flag == True:
39                 print('找第三方问问价格行情')
40                 ret = fn(*args,**kwargs)
41                 print('买到装备')
42                 return ret
43             else:
44                 ret = fn(*args,**kwargs)
45                 return ret
46         return inner
47     return wrapper
48 #语法糖,@装饰器
49 @wrapper_out(True)
50 def func(a,b):  #被wrapper装饰
51     print(a,b)
52     print('开黑')
53     return 'func返回值'
54 abc = func('我是参数1','我是参数2')
55 print(abc)
56 结果
57 找第三方问问价格行情
58 我是参数1 我是参数2
59 开黑
60 买到装备
61 func返回值
62 
63 
64 事例3
65 def getzsq(char):
66     def zsq(func):
67         def inner():
68             print(char * 30)
69             func()
70         return inner
71     return zsq
72 
73 @getzsq('-')
74 def f1():
75     print(666)
76 f1()
77 结果
78 ------------------------------
79 666

多个装饰器同用一个函数

 1 装饰器从上到下装饰,从下到上执行
 2 事例1
 3 def zhuangshiqi_line(func):
 4     def inner():
 5         print('-'*30)
 6         func()
 7         print('-'*10)
 8     return inner
 9 
10 def zhuangshiqi_start(func):
11     def inner():
12         print('*'*30)
13         func()
14         print('*'*10)
15     return inner
16 
17 @zhuangshiqi_line
18 @zhuangshiqi_start
19 def print_content():
20     print('猜猜我是谁')
21 print_content()
22 结果
23 ------------------------------
24 ******************************
25 猜猜我是谁
26 **********
27 ----------
28 
29 
30 事例2
31 def wrapper1(fn):
32     def inner(*args,**kwargs):
33         print('wrapper1-1')
34         ret = fn(*args,**kwargs)
35         print('wrapper1-2')
36         return ret
37     return inner
38 
39 def wrapper2(fn):
40     def inner(*args,**kwargs):
41         print('wrapper2-1')
42         ret = fn(*args,**kwargs)
43         print('wrapper2-2')
44         return ret
45     return inner
46 
47 def wrapper3(fn):
48     def inner(*args,**kwargs):
49         print('wrapper3-1')
50         ret = fn(*args,**kwargs)
51         print('wrapper3-2')
52         return ret
53     return inner
54 @wrapper1
55 @wrapper2
56 @wrapper3
57 def func():
58     print('我是测试小白')
59 func()
60 结果
61 wrapper1-1
62 wrapper2-1
63 wrapper3-1
64 我是测试小白
65 wrapper3-2
66 wrapper2-2
67 wrapper1-2

 

posted @ 2018-11-06 00:29  LinuxCBB  阅读(230)  评论(0)    收藏  举报