zoukankan      html  css  js  c++  java
  • 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 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 123
    17 压子弹
    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')     #传递了两个参数给装饰器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 事例2
    33 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 ret
    41             else:
    42                 ret = fn(*args,**kwargs)
    43                 return ret
    44         return inner
    45     return wrapper
    46 #语法糖,@装饰器
    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 我是参数2
    57 开黑
    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 ret
    15     return inner
    16 
    17 def wrapper3(fn):
    18     def inner(*args,**kwargs):
    19         print('wrapper3-1')
    20         ret = fn(*args,**kwargs)
    21         print('wrapper3-2')
    22         return ret
    23     return inner
    24 @wrapper1
    25 @wrapper2
    26 @wrapper3
    27 def func():
    28     print('我是测试小白')
    29 func()
    30 结果
    31 wrapper1-1
    32 wrapper2-1
    33 wrapper3-1
    34 我是测试小白
    35 wrapper3-2
    36 wrapper2-2
    37 wrapper1-2
  • 相关阅读:
    RestTemplate方法总结
    服务器上获取不到header中设置的自定义的属性值
    记录一次 事务问题 的处理
    java 集合按照 数值排序,如果数值一致 按照名字首字母排序
    mysql中按照中文首字母A-Z排序
    java 关于小数 换算整数 的一点想法
    mysql 根据身份证查询 年龄 性别
    MySQL普通索引(BTREE索引)以及最左前缀匹配
    net.sf.json的常用api
    Object划分
  • 原文地址:https://www.cnblogs.com/selina1997/p/10146401.html
Copyright © 2011-2022 走看看