开闭原则:
在不修改原函数及其调用方式的情况下对原函数功能进行扩展
对代码的修改是封闭
不能修改被装饰的函数的源代码
不能修改被装饰的函数的调用方式
用函数的方式设想一下游戏里用枪的场景
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 pass13 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()传参过来等于fn21 第四步执行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函数内存地址当做返回值返回给@sight47 第五步执行game()相当相当于执行inner()函数,因为@sight相当于game = sight(game)48 第六步打印'瞄准镜49 第七步执行fn函数,因为fn等于game函数,所以会执行game()函数,打印'压子弹','上膛','发射子弹'.fn()函数执行完毕50 第八步打印'跑路'51 第九步然后把所有打印的结果返回给game()52 53 结果54 安装专业狙击瞄准镜55 压子弹56 枪上膛57 发射子弹58 跑路
一个简单的装饰器实现
1 def warpper(fn): 2 def inner(): 3 print('每次执行被装饰函数之前都要先经过这里') 4 fn() 5 return inner 6 @warpper 7 def func(): 8 print('执行了func函数') 9 func()10 11 结果12 每次执行被装饰函数之前都要先经过这里13 执行了func函数
带有一个或多个参数的装饰器
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 12317 压子弹18 枪上膛19 发射子弹20 跑路
动态传递一个或多个参数给装饰器
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') #传递了两个参数给装饰器sight15 print(ret)16 17 @sight18 def car(qq):19 print('登陆QQ号%s'%qq)20 print('开始战车游戏')21 ret2 = car(110110) #传递了一个参数给装饰器sight22 print(ret2)23 结果24 开始游戏25 登陆游戏用户名密码: bob 12326 压子弹27 枪上膛28 发射子弹29 跑路30 None31 开始游戏32 登陆QQ号11011033 开始战车游戏34 跑路35 None36 你会发现这两个函数执行的返回值都为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 ret12 return inner13 @sight14 def game(user,pwd):15 print('登陆游戏用户名密码:',user,pwd)16 print('压子弹')17 print('枪上膛')18 print('发射子弹')19 return '游戏展示完毕'20 ret = game('bob','123') #传递了两个参数给装饰器sight21 print(ret)22 结果23 开始游戏24 登陆游戏用户名密码: bob 12325 压子弹26 枪上膛27 发射子弹28 跑路29 游戏展示完毕30 31 32 事例233 def wrapper_out(flag): #装饰器本身的参数34 def wrapper(fn): #目标函数35 def inner(*args,**kwargs): #目标函数需要接受的参数36 if flag == True:37 print('找第三方问问价格行情')38 ret = fn(*args,**kwargs)39 print('买到装备')40 return ret41 else:42 ret = fn(*args,**kwargs)43 return ret44 return inner45 return wrapper46 #语法糖,@装饰器47 @wrapper_out(True)48 def func(a,b): #被wrapper装饰49 print(a,b)50 print('开黑')51 return 'func返回值'52 abc = func('我是参数1','我是参数2')53 print(abc)54 结果55 找第三方问问价格行情56 我是参数1 我是参数257 开黑58 买到装备59 func返回值
多个装饰器同用一个函数
1 def wrapper1(fn): 2 def inner(*args,**kwargs): 3 print('wrapper1-1') 4 ret = fn(*args,**kwargs) 5 print('wrapper1-2') 6 return ret 7 return inner 8 9 def wrapper2(fn):10 def inner(*args,**kwargs):11 print('wrapper2-1')12 ret = fn(*args,**kwargs)13 print('wrapper2-2')14 return ret15 return inner16 17 def wrapper3(fn):18 def inner(*args,**kwargs):19 print('wrapper3-1')20 ret = fn(*args,**kwargs)21 print('wrapper3-2')22 return ret23 return inner24 @wrapper125 @wrapper226 @wrapper327 def func():28 print('我是测试小白')29 func()30 结果31 wrapper1-132 wrapper2-133 wrapper3-134 我是测试小白35 wrapper3-236 wrapper2-237 wrapper1-2