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

浙公网安备 33010602011771号