为了账号安全,请及时绑定邮箱和手机立即绑定

python装饰器

标签:
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     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

复制代码

原文出处:https://www.cnblogs.com/selina1997/p/10146401.html  

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消