zoukankan      html  css  js  c++  java
  • Python-装饰器

    1、无参装饰器:

      举例1:

    1 def add(x, y):
    2     return x + y
    3 需求后:
    4 def add(x, y):
    5     print('{}.{}'.format(x, y))
    6     return x + y

        需求:一个加法函数添加一些功能,如果向上面那样,但是上面所示是一种硬编码,也就是说,每次都会出现打印,而不是在需要的时候添加。耦合度太高!

      ***举例2:装饰器的引入:

      1 NO 1
      2 def add(x, y):
      3     return x + y
      4 
      5 def add(x, y):
      6     print('{}.{}'.format(x, y))
      7     return x + y
      8 
      9 
     10 NO 2 将 要修饰的内容剥离出来,被修饰的函数分开,在修饰函数里调用被修饰的函数,这样,就不会修改业务函数
     11 def add(x, y):
     12     return  x + y
     13 
     14 def sub(x, y):
     15     return  x - y
     16 
     17 def logger(fn, x, y):
     18     print('function {}, x-{}, y-{}'.format(fn.__name__, x, y))
     19     ret = fn(4, 5)
     20     return ret
     21 
     22 print('result = {}'.format(logger(add, 4, 5)))
     23 print('result = {}'.format(logger(sub, 4, 5)))
     24 
     25 
     26 function add, x-4, y-5
     27 result = 9
     28 function sub, x-4, y-5
     29 result = -1
     30 
     31 
     32 
     33 
     34 
     35 NO 3 # 可以传递各种参数
     36 
     37 def add(x, y):
     38     return  x + y
     39 
     40 def sub(x, y):
     41     return  x - y
     42 
     43 def a(x, y):
     44     pass
     45 
     46 def b(m, n, *args, y, x, **kwargs):
     47     pass
     48 
     49 
     50 def logger(fn, *args, **kwargs): # 这里是形参定义,在这里只是对实参的中转站,不作处理
     51     print('function {}, x-{}, y-{}'.format(fn.__name__, 4, 5))
     52     ret = fn(*args, **kwargs) # 参数解构,这里才是进入相应的业务,比如进入加法,至于数据合法与否也是这里判断的
     53     return ret
     54 
     55 
     56 print('result = {}'.format(logger(add, 4, 5)))
     57 print('result = {}'.format(logger(b, 3,4,5,6,x = 7,y = 8)))
     58 
     59 
     60 function add, x-4, y-5
     61 result = 9
     62 function b, x-4, y-5
     63 result = None
     64 
     65 
     66 NO 4 将上述 修饰函数柯里化
     67 
     68 def add(x, y):
     69     return  x + y
     70 
     71 def sub(x, y):
     72     return  x - y
     73 
     74 def a(x, y):
     75     pass
     76 
     77 def b(m, n, *args, y, x, **kwargs):
     78     pass
     79 
     80 
     81 def logger(fn): # 这里是形参定义,在这里只是对实参的中转站,不作处理
     82     def inner(*args, **kwargs):
     83         print('function {}, x-{}, y-{}'.format(fn.__name__, 4, 5))
     84         ret = fn(*args, **kwargs) # 参数解构,这里才是进入相应的业务,比如进入加法,至于数据合法与否也是这里判断的
     85         return ret
     86     return inner
     87 
     88 # print(logger(add)(4,5))
     89 
     90 add = logger(add) #在add 覆盖之前,已经将add对应的函数赋值给fn这个辨识符
     91 
     92 print(add(4,5)) # 此时就像调用add() 函数本身!!
     93 
     94 
     95 NO 5 装饰器上场   @函数名
     96 
     97 def logger(fn): # 这里是形参定义,在这里只是对实参的中转站,不作处理
     98     def inner(*args, **kwargs):
     99         print('function {}, x-{}, y-{}'.format(fn.__name__, 4, 5))
    100         ret = fn(*args, **kwargs) # 参数解构,这里才是进入相应的业务,比如进入加法,至于数据合法与否也是这里判断的
    101         return ret
    102     return inner
    103 
    104 @logger # add = logger(add)  ----> add = inner
    105 def add(x, y):
    106     return  x + y
    107 
    108 print(add(4, 5)) # 这的add就是inner

        这种装饰器:

        • 它是一个函数
        • 函数作为他的形参
        • 返回值也是一个函数
        • 可以使用@函数名方式,简化
          • 此处总结不准确,只是方便理解

        装饰器 和 高阶函数:

            装饰器是高阶函数,但装饰器是对传入的函数功能的装饰(功能增强)

        举例3: 函数功能增强

     1 #  NO 1 装饰器增强:增加一个时间计时器
     2 
     3 import datetime
     4 
     5 def logger(fn): # 这里是形参定义,在这里只是对实参的中转站,不作处理
     6     def inner(*args, **kwargs):
     7         print('执行业务功能之前')
     8         start = datetime.datetime.now()
     9 
    10         ret = fn(*args, **kwargs) # 参数解构,这里才是进入相应的业务,比如进入加法,至于数据合法与否也是这里判断的
    11 
    12         delta = (start - datetime.datetime.now()).total_seconds()
    13 
    14         print('{}-{}'.format(fn.__name__,delta))
    15         print('执行业务功能之后')
    16         
    17         return ret
    18     return inner
    19 
    20 @logger # add = logger(add)  ----> add = inner
    21 def add(x, y):
    22     return  x + y
    23 
    24 
    25 print(add(3,4))

    2、有参装饰器:

      1 # #有参装饰器
      2 
      3 # no 1 文档字符串
      4 import datetime
      5 
      6 def logger(fn): # 这里是形参定义,在这里只是对实参的中转站,不作处理
      7     def wrapper(*args, **kwargs):
      8         ''' this is wrapper function'''
      9 
     10         print('执行业务功能之前')
     11         start = datetime.datetime.now()
     12 
     13         ret = fn(*args, **kwargs) # 参数解构,这里才是进入相应的业务,比如进入加法,至于数据合法与否也是这里判断的
     14 
     15         delta = (start - datetime.datetime.now()).total_seconds()
     16 
     17         print('{}-{}'.format(fn.__name__,delta))
     18         print('执行业务功能之后')
     19 
     20         return ret
     21     return wrapper
     22 
     23 @logger # add = logger(add)  ----> add = inner
     24 def add(x, y):
     25     ''' this is add function'''
     26     return  x + y
     27 
     28 print(add.__name__) #打印的是 wrapper的
     29 print(add.__doc__)
     30 
     31 # wrapper
     32 #  this is wrapper function
     33 
     34 
     35 # # no 2    输出文旦字符串  print(help(add))   当作帮助信息,让别人看懂
     36 import datetime
     37 
     38 def logger(fn): # 这里是形参定义,在这里只是对实参的中转站,不作处理
     39     def wrapper(*args, **kwargs):
     40         ''' this is wrapper function'''
     41 
     42         print('执行业务功能之前')
     43         start = datetime.datetime.now()
     44 
     45         ret = fn(*args, **kwargs) # 参数解构,这里才是进入相应的业务,比如进入加法,至于数据合法与否也是这里判断的
     46 
     47         delta = (start - datetime.datetime.now()).total_seconds()
     48 
     49         print('{}-{}'.format(fn.__name__,delta))
     50         print('执行业务功能之后')
     51 
     52         return ret
     53     return wrapper
     54 
     55 @logger # add = logger(add)  ----> add = inner
     56 def add(x, y):
     57     ''' this is add function'''    # 这个只能放在第一行才能打印!!!!!
     58     return  x + y
     59 
     60 print(add.__name__)
     61 print(add.__doc__)
     62 print('-------------------')
     63 print(help(add))   # 帮助文档,即文档字符串的信息
     64 
     65 
     66 # wrapper
     67 #  this is wrapper function
     68 # -------------------
     69 # Help on function wrapper in module __main__:
     70 #
     71 # wrapper(*args, **kwargs)
     72 #     this is wrapper function
     73 #
     74 # None
     75 
     76 
     77 # no 3 有参装饰器的引入 ,上面的结果,可以看出,自己一定是被装饰过了,自己的信息都变了,别人打开帮助信息并不是自己想看到的
     78 
     79 import datetime
     80 
     81 def logger(fn): # 这里是形参定义,在这里只是对实参的中转站,不作处理
     82 
     83     def wrapper(*args, **kwargs):
     84         ''' this is wrapper function'''
     85         print('执行业务功能之前')
     86         ret = fn(*args, **kwargs) # 参数解构,这里才是进入相应的业务,比如进入加法,至于数据合法与否也是这里判断的
     87         print('执行业务功能之后')
     88         return ret
     89 
     90     wrapper.__name__ = fn.__name__ #
     91     wrapper.__doc__ = fn.__doc__ #
     92     return wrapper
     93 
     94 @logger # add = logger(add)  ----> add = wrapper
     95 def add(x, y):
     96     ''' this is add function'''    # 这个只能放在第一行才能打印!!!!!
     97     return  x + y
     98 
     99 print(add.__name__)
    100 print(add.__doc__)
    101 
    102 # NO 4 变形2
    103 
    104 def logger(fn): # 这里是形参定义,在这里只是对实参的中转站,不作处理
    105     def copy_properties(src, dest):
    106         dest.__name__ = src.__name__ #
    107         dest.__doc__ = src.__doc__ #
    108 
    109     def wrapper(*args, **kwargs):
    110         ''' this is wrapper function'''
    111         print('执行业务功能之前')
    112         ret = fn(*args, **kwargs) # 参数解构,这里才是进入相应的业务,比如进入加法,至于数据合法与否也是这里判断的
    113         print('执行业务功能之后')
    114         return ret
    115     copy_properties(fn, wrapper)
    116     # wrapper.__name__ = fn.__name__ #
    117     # wrapper.__doc__ = fn.__doc__ #
    118     return wrapper
    119 
    120 @logger # add = logger(add)  ----> add = wrapper
    121 def add(x, y):
    122     ''' this is add function'''    # 这个只能放在第一行才能打印!!!!!
    123     return  x + y
    124 
    125 print(add.__name__)
    126 print(add.__doc__)
    127 
    128 # NO 5 变现3  将修改函数放全局中,并将其柯里化
    129 
    130 def copy_properties(src):
    131     def _copy(dest):
    132         dest.__name__ = src.__name__ #
    133         dest.__doc__ = src.__doc__ #
    134     return _copy
    135 
    136 
    137 def logger(fn):
    138 
    139 
    140     def wrapper(*args, **kwargs):
    141         ''' this is wrapper function'''
    142         print('执行业务功能之前')
    143         ret = fn(*args, **kwargs)
    144         print('执行业务功能之后')
    145         return ret
    146 
    147     copy_properties(fn)( wrapper)  # 因为copy_properties() 被柯里化了
    148     # wrapper.__name__ = fn.__name__ #
    149     # wrapper.__doc__ = fn.__doc__ #
    150     return wrapper
    151 
    152 @logger # add = logger(add)  ----> add = wrapper
    153 def add(x, y):
    154     ''' this is add function'''    # 这个只能放在第一行才能打印!!!!!
    155     return  x + y
    156 
    157 print(add.__name__)
    158 print(add.__doc__)
    159 
    160 
    161 
    162 # NO 6 使用装饰器将其改造,相当于说,给wrapper()函数增加一个装饰器使其能够实现上面的功能
    163 # # 也就是说修改 wrapper的文档字符串,改为业务函数的文档字符串
    164 def copy_properties(src): # 要理解柯里化的调用方式
    165     def _copy(dest):
    166         dest.__name__ = src.__name__ # src 和dest 都是函数名
    167         dest.__doc__ = src.__doc__ #
    168         return dest
    169     return _copy
    170 
    171 def logger(fn):
    172     #    @copy_properties   # wrapper=copy_properties(wrapper) wrapper=_copy  此时执行_copy()是缺少参数的,也就是说,这样的不对的
    173     @copy_properties(fn)  #  # 等价于这句话 copy_properties(fn)( wrapper) ,上面的版本的调用方式
    174     # wrapper=copy_properties(fn)(wrapper)  ---> wrapper = _copy --->_copy(wrapper)---->因为此时的wrapper已经变为_copy,所以还要将其变为原来的,当然是修改了字符串文档后的,所以_copy()返回dest,即wrapper
    175     def wrapper(*args, **kwargs):
    176         ''' this is wrapper function'''
    177         print('执行业务功能之前')
    178         ret = fn(*args, **kwargs)
    179         print('执行业务功能之后')
    180         return ret
    181     return wrapper
    182 
    183 @logger # add = logger(add)  ----> add = wrapper
    184 def add(x, y):
    185     ''' this is add function'''    # 这个只能放在第一行才能打印!!!!!
    186     return  x + y
    187 
    188 print(add.__name__)
    189 print(add.__doc__)
    190 
    191 
    192 # NO7 上面只是练习,事实上,是有模块的,处理字符串文档的,使用  update_wrapper(w, fn)
    193 from functools import update_wrapper
    194 
    195 def logger(fn):
    196     def w(*args, **kwargs):
    197         ''' this is wrapper function'''
    198         print('执行业务功能之前')
    199         ret = fn(*args, **kwargs)
    200         print('执行业务功能之后')
    201         return ret
    202     update_wrapper(w, fn) # 参数前者是修饰,后者是被修饰,它会return w,所以下面不用return w
    203     # return w
    204 
    205 @logger # add = logger(add)  ----> add = wrapper
    206 def add(x, y):
    207     ''' this is add function'''    # 这个只能放在第一行才能打印!!!!!
    208     return  x + y
    209 
    210 print(add.__name__)
    211 print(add.__doc__)
    212 
    213 # NO 8 类似update_wrapper的柯里化wraps 偏函数
    214 from functools import update_wrapper,wraps
    215 
    216 def logger(fn):
    217     @wraps(fn) # w=wraps(fn)(w)结构就是一个柯里化的样式
    218     def w(*args, **kwargs):
    219         ''' this is wrapper function'''
    220         print('执行业务功能之前')
    221         ret = fn(*args, **kwargs)
    222         print('执行业务功能之后')
    223         return ret
    224     # update_wrapper(w, fn)
    225 
    226     return w
    227 
    228 @logger # add = logger(add)  ----> add = wrapper
    229 def add(x, y):
    230     ''' this is add function'''    # 这个只能放在第一行才能打印!!!!!
    231     return  x + y
    232 
    233 print(add.__name__)
    234 print(add.__doc__)
    235 print(add.__dict__)
    236 print(add.__module__)
    237 print(add.__qualname__)
    238 print(add.__annotations__)
    239 
    240 # add
    241 #  this is add function
    242 # {'__wrapped__': <function add at 0x0000000001EC3378>}
    243 # __main__
    244 # add
    245 # {}
    246 
    247 '''
    248 WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__',
    249                        '__annotations__')
    250 WRAPPER_UPDATES = ('__dict__',)
    251 def update_wrapper(wrapper,
    252                    wrapped,
    253                    assigned = WRAPPER_ASSIGNMENTS,
    254                    updated = WRAPPER_UPDATES):
    255     """Update a wrapper function to look like the wrapped function
    256 
    257        wrapper is the function to be updated
    258        wrapped is the original function
    259        assigned is a tuple naming the attributes assigned directly
    260        from the wrapped function to the wrapper function (defaults to
    261        functools.WRAPPER_ASSIGNMENTS)
    262        updated is a tuple naming the attributes of the wrapper that
    263        are updated with the corresponding attribute from the wrapped
    264        function (defaults to functools.WRAPPER_UPDATES)
    265     """
    266     for attr in assigned:
    267         try:
    268             value = getattr(wrapped, attr) # 拿属性 ,这里的wrapped 就是 add
    269         except AttributeError:
    270             pass
    271         else:
    272             setattr(wrapper, attr, value) # 设置属性,wrapper
    273     for attr in updated:
    274         getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
    275     # Issue #17482: set __wrapped__ last so we don't inadvertently copy it
    276     # from the wrapped function when updating __dict__
    277     wrapper.__wrapped__ = wrapped  # 在w 函数上加了一个新的属性 fn
    278     # Return the wrapper so this can be used as a decorator via partial()
    279     return wrapper # 这个很重要,就是w = w,没有就出现大问题
    280 
    281 def wraps(wrapped,  ---------------这是一个偏函数 # 就剩wrapper 没有给定
    282           assigned = WRAPPER_ASSIGNMENTS,
    283           updated = WRAPPER_UPDATES):
    284     """Decorator factory to apply update_wrapper() to a wrapper function
    285 
    286        Returns a decorator that invokes update_wrapper() with the decorated
    287        function as the wrapper argument and the arguments to wraps() as the
    288        remaining arguments. Default arguments are as for update_wrapper().
    289        This is a convenience function to simplify applying partial() to
    290        update_wrapper().
    291     """
    292     return partial(update_wrapper, wrapped=wrapped,
    293                    assigned=assigned, updated=updated)
    294 
    295 '''
    296 
    297 # NO 9 增强装饰器
    298 import datetime
    299 import  time
    300 from functools import update_wrapper,wraps
    301 
    302 def logger(fn, m=5):
    303     @wraps(fn) # w=wraps(fn)(w)结构就是一个柯里化的样式
    304     def w(*args, **kwargs):
    305         ''' this is wrapper function'''
    306         start = datetime.datetime.now()
    307         ret = fn(*args, **kwargs)
    308         delta = (start - datetime.datetime.now()).total_seconds()
    309         if delta < m:
    310             print("++++++")
    311         else:
    312             print('------')
    313         return ret
    314     return w
    315 
    316 @logger # add = logger(add)  ----> add = wrapper
    317 def add(x, y):
    318     ''' this is add function'''  # 这个只能放在第一行才能打印!!!!!
    319     time.sleep(7)
    320     return  x + y
    321 
    322 print(add(3,4))
    323 
    324 # NO 10 上面的函数,参数m 必须是缺省值,所以不灵活,所以继续修改,柯梨花
    325 import datetime
    326 import  time
    327 from functools import update_wrapper,wraps
    328 
    329 def logger(m): # 这样的话,可以随便传入更多的参数,一般最多三层,因为最终要回到_logger上
    330     def _logger(fn):
    331         @wraps(fn) # w=wraps(fn)(w)结构就是一个柯里化的样式
    332         def w(*args, **kwargs):
    333             ''' this is wrapper function'''
    334             start = datetime.datetime.now()
    335             ret = fn(*args, **kwargs)
    336             delta = (start - datetime.datetime.now()).total_seconds()
    337             if delta < m:
    338                 print("++++++")
    339             else:
    340                 print('------')
    341             return ret
    342         return w
    343     return _logger
    344 
    345 
    346 @logger(3) # add = logger(3)(add)  ----> add = wrapper
    347 def add(x, y):
    348     ''' this is add function'''  # 这个只能放在第一行才能打印!!!!!
    349     time.sleep(2)
    350     return  x + y
    351 
    352 print(add(4,5))
    353 
    354 # no 11,将内部的一下功能提取出来,比如:写入日志,否则得修改原码
    355 import datetime
    356 import  time
    357 from functools import update_wrapper,wraps
    358 
    359 def logger(m,
    360            func=lambda name, took:print('function {} took {}s'.format(name, took))):# 加入打印到屏幕上
    361     def _logger(fn):
    362         @wraps(fn) # w=wraps(fn)(w)结构就是一个柯里化的样式
    363         def w(*args, **kwargs):
    364             ''' this is wrapper function'''
    365             start = datetime.datetime.now()
    366             ret = fn(*args, **kwargs)
    367             delta = (start - datetime.datetime.now()).total_seconds()
    368             if delta < m:
    369                 func(fn.__name__, delta) # 将这里的功能提取出来,放到最外面,作为缺省参数
    370             else:
    371                 print('------')
    372             return ret
    373         return w
    374     return _logger
    375 
    376 
    377 @logger(3) # add = logger(3)(add)  ----> add = wrapper
    378 def add(x, y):
    379     ''' this is add function'''  # 这个只能放在第一行才能打印!!!!!
    380     time.sleep(2)
    381     return  x + y
    382 
    383 print(add(4,5))
    384 
    385 
    386 
    387 # no 12  如果缺省参数表示的函数不能一行搞定,也可以写最外面,然后调用
    388 import datetime
    389 import  time
    390 from functools import update_wrapper,wraps
    391 
    392 def toLog(name, took):# 加入写到日志中
    393     pass
    394 
    395 
    396 def logger(m,func=toLog):# 这里写成缺省,如果要写到其他地方,可以在@looger()中添加位置参数,覆盖这个缺省参数
    397     def _logger(fn):
    398         @wraps(fn) # w=wraps(fn)(w)结构就是一个柯里化的样式
    399         def w(*args, **kwargs):
    400             ''' this is wrapper function'''
    401             start = datetime.datetime.now()
    402             ret = fn(*args, **kwargs)
    403             delta = (start - datetime.datetime.now()).total_seconds()
    404             if delta < m:
    405                 func(fn.__name__, delta) # 将这里的功能提取出来,放到最外面,作为缺省参数
    406             else:
    407                 print('------')
    408             return ret
    409         return w
    410     return _logger
    411 
    412 
    413 @logger(3) # add = logger(3)(add)  ----> add = wrapper
    414 def add(x, y):
    415     ''' this is add function'''  # 这个只能放在第一行才能打印!!!!!
    416     time.sleep(2)
    417     return  x + y
    418 
    419 print(add(4,5))

         注意

     1 from functools import wraps
     2 
     3 def logger(fn):
     4     print('------------')
     5     @wraps(fn)
     6     def wrapper(*args, **kwargs):
     7         print(add.__annotations__, '---')
     8         set = fn(*args, **kwargs)
     9         return  set
    10     print("+++++++++++++")
    11     return wrapper
    12 
    13 @logger # add = logger(add) = wrapper
    14 def add(x:int, y:int) -> int:# 返回值的注解
    15     return  x + y
    16 
    17 @logger # sub = logger(sub) = wrapper
    18 def sub(x, y):
    19     return x - y
    20 '''
    21 直接运行,可以看到是执行了的,一分析便可以看出,虽然add,sub没有调用,但是
    22 @ 语法糖是要调用logger的,所以函数会执行。
    23 其次,当执行add(3, 4) 时,wrapper的字符串文档会被add的覆盖,在执行sub(3, 4)
    24 的时候,同样,wrapper的字符串文档也会被sub的覆盖,但是两者没有影响,是因为,logger
    25 是一个全局函数,而wrapper是一个嵌套函数,也就是说是一个局部变量,所以执行add 和sub
    26 的时候,在不同的栈里,全局函数是一个,先压栈,执行add 的时候,压wrapper,执行完后,弹出,在压
    27 sub对应的wrapper,所以是互不影响的。
    28 线程调用函数,线程跟栈有关,对创建对象(堆是散放的),栈是对象的引用'''
    29 # ------------
    30 # +++++++++++++
    31 # ------------
    32 # +++++++++++++

      

    为什么要坚持,想一想当初!
  • 相关阅读:
    如何改变checkbox的样式
    EChart.js 简单入门
    Javascript异步编程的4种方法
    手写手机网站
    Handlebars的基本用法
    装饰器
    using Newtonsoft.Json;
    chrome插件学习笔记
    绩效考核如何快速评分
    wx jssdk
  • 原文地址:https://www.cnblogs.com/JerryZao/p/9545444.html
Copyright © 2011-2022 走看看