• 装饰器


    一、装饰器的作用

    装饰器实际上就是为了给某程序增添功能,但该程序已经上线或已经被使用,那么就不能大批量的修改源代码,这样是不科学的也是不现实的,因为就产生了装饰器,使得其满足:

    1. 不能修改被装饰的函数的源代码
    2. 不能修改被装饰的函数的调用方式
    3. 满足1、2的情况下给程序增添功能

    概括的讲,装饰器的作用就是在不修改原代码的条件下为已经存在的函数或对象添加功能

    二、装饰器实现

    写一个装饰器记录函数执行的时间

    1、最简单的装饰器,不带参数的

    两层函数,外层函数包裹内层函数,内层函数调用了外层函数的变量,外层函数返回内层函数的函数名;被装饰函数作为参数传入装饰器。

    import time
    
    def deco(func):
        def wrapper():
            startTime = time.time()
            func()
            endTime = time.time()
            msecs = (endTime - startTime)*1000
            print("time is %d ms" %msecs)
        return wrapper
    
    
    @deco
    def func():
        print("hello")
        time.sleep(1)
        print("world")
    
    if __name__ == '__main__':
        f = func 
        f()

    这里的deco函数就是最原始的装饰器,它的参数是一个函数,然后返回值也是一个函数。其中作为参数的这个函数func()就在返回函数wrapper()的内部执行。然后在函数func()前面加上@deco,func()函数就相当于被注入了计时功能,现在只要调用func(),它就已经变身为“新的功能更多”的函数了。
    所以这里装饰器就像一个注入符号:有了它,拓展了原来函数的功能既不需要侵入函数内更改代码,也不需要重复执行原函数。

    装饰带参数函数的装饰器:

    #带有参数的装饰器
    import time
    
    def deco(func):
        def wrapper(a,b):
            startTime = time.time()
            func(a,b)
            endTime = time.time()
            msecs = (endTime - startTime)*1000
            print("time is %d ms" %msecs)
        return wrapper
    
    
    @deco
    def func(a,b):
        print("hello,here is a func for add :")
        time.sleep(1)
        print("result is %d" %(a+b))
    
    if __name__ == '__main__':
        f = func
        f(3,4)
        #func()

    装饰带有不定参数函数的装饰器:

    #带有不定参数的装饰器
    import time
    
    def deco(func):
        def wrapper(*args, **kwargs):
            startTime = time.time()
            func(*args, **kwargs)
            endTime = time.time()
            msecs = (endTime - startTime)*1000
            print("time is %d ms" %msecs)
        return wrapper
    
    
    @deco
    def func(a,b):
        print("hello,here is a func for add :")
        time.sleep(1)
        print("result is %d" %(a+b))
    
    @deco
    def func2(a,b,c):
        print("hello,here is a func for add :")
        time.sleep(1)
        print("result is %d" %(a+b+c))
    
    
    if __name__ == '__main__':
        f = func
        func2(3,4,5)
        f(3,4)
        #func()

    2、带参数的装饰器

    给装饰器传参:

      再提一层函数. 专门给装饰器传递参数用的.

    当装饰器装饰函数后元数据会发生改变如何保护元数据呢: 给内存函数加@wraps(func)装饰器 

     1 from functools import wraps
     2 
     3 def wrapper_out(flag):
     4     def wrapper(func):
     5         @wraps(func)  # 保护元数据
     6         def inner(*args, **kwargs):
     7             if flag:
     8                 result = func(*args, **kwargs)
     9                 print(func.__name__)  # test
    10                 return result
    11             else:
    12                 print("by by")
    13         return inner
    14     return wrapper
    15 
    16 @wrapper_out(1)  #@wrapper, flag->1  ==> test=wrapper(test),==> test=inner, flag->1,func->test
    17 def test():
    18     print("I'm test")
    19 
    20 test()
    21 print(test.__name__)  # 没有加@wraps(func)时打印inner, 加了打印test

    @wrapper_out(1) 先执行后面的括号, 返回一个装饰器再和前面的@拼接.成一个语法糖

    3、多个装饰器装饰一个函数

    import time
    
    def deco01(func):
        def wrapper(*args, **kwargs):
            print("this is deco01")
            startTime = time.time()
            func(*args, **kwargs)
            endTime = time.time()
            msecs = (endTime - startTime)*1000
            print("time is %d ms" %msecs)
            print("deco01 end here")
        return wrapper
    
    def deco02(func):
        def wrapper(*args, **kwargs):
            print("this is deco02")
            func(*args, **kwargs)
    
            print("deco02 end here")
        return wrapper
    
    @deco01
    @deco02
    def func(a,b):
        print("hello,here is a func for add :")
        time.sleep(1)
        print("result is %d" %(a+b))
    
    
    
    if __name__ == '__main__':
        f = func
        f(3,4)
        #func()
    
    '''
    this is deco01
    this is deco02
    hello,here is a func for add :
    result is 7
    deco02 end here
    time is 1003 ms
    deco01 end here
    '''

    上面例子中;当装饰器装载时,func函数被作为参数传给deco02,而deco02又作为参数传给deco01,装载装饰器相当于执行func=deco01(deco02(func)), 此时先执行deco02(func)定义了deco02中的wrapper函数将func指向被装饰的函数,并返回wrapper,执行deco01(wrapper)将func指向deco02的wrapperb并返回返回deco01的wrapper函数;然后进行赋值func=wrapper ,用函数替代了函数名func,当执行func时相当于开始执行deco01的wrapper, 打印“this is deco01” 当执行到deco01中的func(*args, **kwargs)时实际上执行的是deco02中的wrapper打印"this is deco02",当执行到deco02的func(*args, **kwargs)实际上执行的是func函数打印“hello,here is a func for add :”和“result is 7”,执行完func 继续往下执行打印“deco02 end here”,然后deco01中wrapper继续往下执行打印“”time is 1003ms“” 和 “”“deco01 end here”

    理解多个装饰器另一个更简单的例子:

     1 def dec1(func):  
     2     print("1111")  
     3     def one():  
     4         print("2222")  
     5         func()  
     6         print("3333")  
     7     return one  
     8 
     9 def dec2(func):  
    10     print("aaaa")  
    11     def two():  
    12         print("bbbb")  
    13         func()  
    14         print("cccc")  
    15     return two  
    16 
    17 @dec1  
    18 @dec2  
    19 def test():  
    20     print("test test")  
    21 
    22 test()  

    输出:

    aaaa  
    1111  
    2222  
    bbbb  
    test test  
    cccc  
    3333

    装饰器的外函数和内函数之间的语句是没有装饰到目标函数上的,而是在装载装饰器时的附加操作。 17~20行是装载装饰器的过程,相当于执行了test=dect1(dect2(test)),此时先执行dect2(test),结果是输出aaaa、将func指向函数test、并返回函数two,然后执行dect1(two),结果是输出1111、将func指向函数two、并返回函数one,然后进行赋值,用函数替代了函数名test。 22行则是实际调用被装载的函数,这时实际上执行的是函数one,运行到func()时执行函数two,再运行到func()时执行未修饰的函数test。

     因此多个装饰器装饰一个函数的执行顺序为:

    @wrapper_1
    @wrapper_2
    @wrapper_3
    def yue():
      print("")
    
    yue()

    # wrapper_1 wrapper_2 wrapper_3  yue  wrapper_3 wrapper_2  wrapper_1 ​

    类似于{ [ ( 0 ) ] }  

  • 相关阅读:
    HDFS、YARN、Mapreduce简介
    List<object> 转 List<T>
    CTR+A组合键 以及终止按键事件传递
    BackgroundWorker 的输入、输出参数、进度条与文字刷新、取消机制、返回事件
    读取Excel文件的两种方法比较 以及用NPOI写入Excel
    浅复制不能传递,重新赋值就重新浅复制
    gridControl添加右键菜单
    C#设置Excel行高、列宽
    任意字符串(包括空串)都包含空白字符串
    JAVA 在程序中存储和修改信息
  • 原文地址:https://www.cnblogs.com/zwq-/p/9932646.html
Copyright © 2020-2023  润新知