开闭原则:
在不修改原函数及其调用方式的情况下对原函数功能进行扩展
对代码的修改是封闭
不能修改被装饰的函数的源代码
不能修改被装饰的函数的调用方式
用函数的方式设想一下游戏里用枪的场景
def game(): print('压子弹') print('枪上膛') print('发射子弹') game() game() game() 此时需要给枪增加一个瞄准镜,比如狙击远程目标时候需要加,狙击近程目标不用加 此时上边的代码就变成了现在的代码 def sight(): print('专业狙击瞄准镜') game() sight() sight() sight() 此时的设计就不符合开闭原则(因为修改了原代码及调用名称)
装饰器(python里面的动态代理)
本质: 是一个闭包
组成: 函数+实参高阶函数+返回值高阶函数+嵌套函数+语法糖 = 装饰器
存在的意义: 在不破坏原有函数和原有函数调用的基础上,给函数添加新的功能
通用装饰器写法:
def warpper(fn): # fn是目标函数相当于func def inner(*args,**kwargs): # 为目标函数的传参 '''在执行目标函数之前操作''' ret = fn(*args,**kwargs) # 调用目标函数,ret是目标函数的返回值 '''在执行目标函数之后操作''' return ret # 把目标函数返回值返回,保证函数正常的结束 return inner #语法糖 @warpper #相当于func = warpper(func) def func(): pass func() #此时就是执行的inner函数
上边的场景用装饰器修改后
方式一 def game(): print('压子弹') print('枪上膛') print('发射子弹') def sight(fn): # fn接收的是一个函数 def inner(): print('安装专业狙击瞄准镜') fn() #调用传递进来的函数 print('跑路') return inner #返回函数地址 game = sight(game) #传递game函数到sight函数中 game() 执行步骤 第一步定义两个函数game()为普通函数,sight()为装饰器函数 第二步定义game = sight(game)等于把game函数当做参数传递给sight(fn)装饰器函数fn形参 第三步执行sight(fn),fn在形参位置,相当于下边函数game()传参过来等于fn 第四步执行inner函数,然后return把inner函数内存地址当做返回值返回给sight(game) 第五步然后执行game(),相当于执行inner函数 第六步,执行inner函数,打印'狙击镜',执行fn()形参,由于fn形参等于game函数,所以执行game()函数,打印'压子弹','上膛','发射子弹' 第七步打印'跑路' 第八步把打印的结果返回给game() 方式二 def sight(fn): # fn接收的是一个函数 def inner(): print('安装专业狙击瞄准镜') fn() #调用传递进来的函数 print('跑路') return inner #返回函数地址 @sight #相当于game = sight(game) def game(): print('压子弹') print('枪上膛') print('发射子弹') game() 执行步骤 第一步执行sight(fn)函数 第二步执行@sight,相当于把把game函数与sight装饰器做关联 第三步把game函数当做参数传递给sight(fn)装饰器函数fn形参 第四步执行inner函数,然后return把inner函数内存地址当做返回值返回给@sight 第五步执行game()相当相当于执行inner()函数,因为@sight相当于game = sight(game) 第六步打印'瞄准镜 第七步执行fn函数,因为fn等于game函数,所以会执行game()函数,打印'压子弹','上膛','发射子弹'.fn()函数执行完毕 第八步打印'跑路' 第九步然后把所有打印的结果返回给game() 结果 安装专业狙击瞄准镜 压子弹 枪上膛 发射子弹 跑路
一个简单的装饰器实现
def warpper(fn): def inner(): print('每次执行被装饰函数之前都要先经过这里') fn() return inner @warpper def func(): print('执行了func函数') func() 结果 每次执行被装饰函数之前都要先经过这里 执行了func函数
带有一个或多个参数的装饰器
def sight(fn): #fn等于调用game函数 def inner(*args,**kwargs): #接受到的是元组("bob",123) print('开始游戏') fn(*args,**kwargs) #接受到的所有参数,打散传递给user,pwd print('跑路') return inner @sight def game(user,pwd): print('登陆游戏用户名密码:',user,pwd) print('压子弹') print('枪上膛') print('发射子弹') game('bob','123') 结果 开始游戏 登陆游戏用户名密码: bob 123 压子弹 枪上膛 发射子弹 跑路
动态传递一个或多个参数给装饰器
def sight(fn): #调用game函数 def inner(*args,**kwargs): #接受到的是元组("bob",123) print('开始游戏') fn(*args,**kwargs) #接受到的所有参数,打散传递给正常的参数 print('跑路') return inner @sight def game(user,pwd): print('登陆游戏用户名密码:',user,pwd) print('压子弹') print('枪上膛') print('发射子弹') return '游戏展示完毕' ret = game('bob','123') #传递了两个参数给装饰器sight print(ret) @sight def car(qq): print('登陆QQ号%s'%qq) print('开始战车游戏') ret2 = car(110110) #传递了一个参数给装饰器sight print(ret2) 结果 开始游戏 登陆游戏用户名密码: bob 123 压子弹 枪上膛 发射子弹 跑路 None 开始游戏 登陆QQ号110110 开始战车游戏 跑路 None 你会发现这两个函数执行的返回值都为None,但是我game定义返回值了return '游戏展示完毕',却没给返回
装饰器的返回值
为什么我定义了返回值,但是返回值还是None呢,是因为我即使在game函数中定义了return '游戏展示完毕' 但是装饰器里只有一个return inner定义返回值,但是这个返回值是返回的inner函数的内存地址的,并不是inner 函数内部的return所以默认为None,所以应该定义一个inner函数内部的return返回值,而且也没有接收返回值的变量, 所以要要设置ret = fn(*args,**kwargs)和return ret def sight(fn): #调用game函数 def inner(*args,**kwargs): #接受到的是元组("bob",123) print('开始游戏') ret = fn(*args,**kwargs) #接受到的所有参数,打散传递给正常的参数 print('跑路') return ret return inner @sight def game(user,pwd): print('登陆游戏用户名密码:',user,pwd) print('压子弹') print('枪上膛') print('发射子弹') return '游戏展示完毕' ret = game('bob','123') #传递了两个参数给装饰器sight print(ret) 结果 开始游戏 登陆游戏用户名密码: bob 123 压子弹 枪上膛 发射子弹 跑路 游戏展示完毕 事例2 def wrapper_out(flag): #装饰器本身的参数 def wrapper(fn): #目标函数 def inner(*args,**kwargs): #目标函数需要接受的参数 if flag == True: print('找第三方问问价格行情') ret = fn(*args,**kwargs) print('买到装备') return ret else: ret = fn(*args,**kwargs) return ret return inner return wrapper #语法糖,@装饰器 @wrapper_out(True) def func(a,b): #被wrapper装饰 print(a,b) print('开黑') return 'func返回值' abc = func('我是参数1','我是参数2') print(abc) 结果 找第三方问问价格行情 我是参数1 我是参数2 开黑 买到装备 func返回值
多个装饰器同用一个函数
def wrapper1(fn): def inner(*args,**kwargs): print('wrapper1-1') ret = fn(*args,**kwargs) print('wrapper1-2') return ret return inner def wrapper2(fn): def inner(*args,**kwargs): print('wrapper2-1') ret = fn(*args,**kwargs) print('wrapper2-2') return ret return inner def wrapper3(fn): def inner(*args,**kwargs): print('wrapper3-1') ret = fn(*args,**kwargs) print('wrapper3-2') return ret return inner @wrapper1 @wrapper2 @wrapper3 def func(): print('我是测试小白') func() 结果 wrapper1-1 wrapper2-1 wrapper3-1 我是测试小白 wrapper3-2 wrapper2-2 wrapper1-2