• 13-Python-装饰器


    1、装饰器的定义

    装饰器的本质就是函数,用来装饰其它函数,就是为其它函数添加附加功能。

    装饰器原则如下:

    • 不能修改被装饰的函数的源代码
    • 不能修改被装饰的函数的调用方式

    2、实现装饰器知识储备

    • 函数即变量
     1 def bar():
     2     print("in the bar")
     3 def foo():
     4     print("in the foo")
     5     bar()
     6 
     7 foo()
     8 
     9 print("----------分割线-----------")
    10 
    11 def foo1():
    12     print("in the foo")
    13     bar1()
    14 def bar1():
    15     print("in the bar")
    16 
    17 foo1()
    18 
    19 print("----------分割线-----------")
    20 # 这样会报错
    21 # def foo2():
    22 #     print("in the foo")
    23 #     bar2()
    24 # foo2()
    25 # def bar2():
    26 #     print("in the bar")
    • 高阶函数
    • 把一个函数名当作实参传递给另外一个函数(在不修改被装饰函数源代码的情况下为其添加功能)
     1 import time
     2 
     3 def bar1():
     4     time.sleep(3)
     5     print("in the bar")
     6 
     7 def test2(func):
     8     start_time = time.time()
     9     func()  # 相当于运行bar1()
    10     stop_time = time.time()
    11     print("total run time %s" %(stop_time - start_time))
    12 
    13 test2(bar1)  
    • 返回值中包含函数名(不能修改函数的调用方式)
     1 def bar2():
     2     time.sleep(3)
     3     print("in the bar2")
     4 
     5 def test3(func):
     6     print(func)
     7     return func
     8 
     9 print(test3(bar2))  # 获取的是内存地址
    10 
    11 res = test3(bar2)
    12 res()
    • 嵌套函数
    1 def foo():
    2     print("in the foo")
    3     def bar():
    4         print("in the bar")
    5     bar()  # 局部变量只能在其作用域内调用
    6 
    7 foo()
     1 x = 0
     2 def grandpa():
     3     x = 1
     4     def dad():
     5         x = 2
     6         def son():
     7             x = 3
     8             print(x)  # 最终打印结果为3
     9         son()
    10     dad()
    11 grandpa()
    • 高阶函数 + 嵌套函数 --》装饰器
     1 import time
     2 
     3 
     4 def timer(func):
     5     def deco():
     6         start_time = time.time()
     7         func()
     8         stop_time = time.time()
     9         print("total time is %s" % (stop_time - start_time))
    10     return deco  # 返回deco()的内存地址
    11 
    12 
    13 def test1():
    14     time.sleep(3)
    15     print("in the test1")
    16 
    17 
    18 def test2():
    19     time.sleep(3)
    20     print("in the test2")
    21 
    22 #以下可直接用装饰器语法代替
    23 timer(test1)  # test1的内存地址赋值给func,返回deco()的内存地址
    24 print(timer(test1))  # 返回deco()的内存地址
    25 test1 = timer(test1)  # 内存地址赋值给test1
    26 test1()  # 相当于执行deco()
    27 
    28 timer(test2)
    29 test2 = timer(test2)
    30 test2()
    31 
    32 print("---------我是分隔符---------")
    33 
    34 
    35 # 装饰器语法如下。(和上面引用的效果一样)
    36 @timer  # 相当于test1 = timer(test1)
    37 def test1():
    38     time.sleep(3)
    39     print("in the test1")
    40 
    41 
    42 @timer  # 相当于test1 = timer(test1)
    43 def test2():
    44     time.sleep(3)
    45     print("in the test2")
    46 
    47 
    48 test1()
    49 test2()

    3、动态参数装饰器

    def timer(bar):
        def inner(*args, **kwargs):
            start_time = time.time()
            foo_ret = bar(*args, **kwargs)
            end_time = time.time()
            used_time = end_time - start_time
            print(used_time)
    
            return foo_ret
    
        return inner
    
    
    @timer
    def foo(*args, **kwargs):
        time.sleep(1)
        print("我的参数:", args, kwargs)
        print("我的运行时间:")
    
        return "我的返回值"
    
    
    ret = foo("动态参数装饰器", (1, 2), name="Druid", age=18)
    print(ret)

      输出结果如下:

      

    4、装饰器原理图解

     4.1 被装饰函数没有返回值

      

     4.2 被装饰函数有返回值

      

       注意:第二步仅为过程分析量,不作为真实的执行顺序。

    5、装饰器固定格式

      装饰器的固定格式如下例所示:

    def wrapper(func):
        """
        该函数为装饰器函数
        :param func: 这里的func参数实质是指向被装饰函数的内存地址
        :return:
        """
        def inner(*args, **kwargs):
            """
            该函数为装饰器函数内部函数
            :param args: 实质接收的是被装饰函数的位置参数
            :param kwargs: 实质接收的是被装饰函数的关键字参数
            :return: 返回的是被装饰函数的返回值
            """
            print("这里放被装饰函数执行之前要做的事")
            func_ret = func(*args, **kwargs)  # 被装饰的函数
            print("这里放被装饰函数执行之后要做的事")
            
            return func_ret
    
        return inner
    
    
    @wrapper  # 等价于my_func = wrapper(my_func)
    def my_func(*args, **kwargs):
        """
        该函数为被装饰函数
        :param args: 接收位置参数
        :param kwargs: 接收关键字参数
        :return: 返回值
        """
        print(*args, **kwargs)
    
        return ret
    
    
    ret = my_func()  # 执行原函数,实质是执行inner()。函数返回值保存在变量ret中。

    6、装饰器修复

      当我们使用装饰器去装饰某个函数时,我们想要引用被装饰函数原私有属性,如__name__、__doc__时,就有问题了,因为我们虽然仍然在执行被装饰函数,但其实执行的是闭包,看下例。

    def wrapper(func):
        """
        该函数为装饰器函数
        :param func: 这里的func参数实质是指向被装饰函数的内存地址
        :return:
        """
        def inner(*args, **kwargs):
            """
            该函数为闭包(装饰器函数内部函数)
            :param args: 实质接收的是被装饰函数的位置参数
            :param kwargs: 实质接收的是被装饰函数的关键字参数
            :return: 返回的是被装饰函数的返回值
            """
    
            func_ret = func(*args, **kwargs)  # 被装饰的函数
    
            return func_ret
    
        return inner
    
    
    @wrapper  # 等价于my_func = wrapper(my_func)
    def my_func(*args, **kwargs):
        """
        该函数为被装饰函数
        :param args: 接收位置参数
        :param kwargs: 接收关键字参数
        :return: 返回值
        """
        print(*args, **kwargs)
    
        return "返回值"
    
    
    my_func("装饰器没被修复前,被装饰函数原函数的私有属性如__name__、__doc__是获取不到的,如下:")  # 执行原函数,实质是执行inner()
    print(my_func.__name__)  # 打印函数的名字
    print(my_func.__doc__)  # 打印函数的注释文档

      输出结果如下:

      

      如果仍想使用被装饰函数的原私有属性,那么就可以用装饰器修复:

    from functools import wraps
    
    
    def wrapper(func):
        """
        该函数为装饰器函数
        :param func: 这里的func参数实质是指向被装饰函数的内存地址
        :return:
        """
    
        @wraps(func)
        def inner(*args, **kwargs):
            """
            该函数为闭包(装饰器函数内部函数)
            :param args: 实质接收的是被装饰函数的位置参数
            :param kwargs: 实质接收的是被装饰函数的关键字参数
            :return: 返回的是被装饰函数的返回值
            """
    
            func_ret = func(*args, **kwargs)  # 被装饰的函数
    
            print("装饰器修复不会改变原装饰器的作用")
    
            return func_ret
    
        return inner
    
    
    @wrapper  # 等价于my_func = wrapper(my_func)
    def my_func(*args, **kwargs):
        """
        该函数为被装饰函数
        :param args: 接收位置参数
        :param kwargs: 接收关键字参数
        :return: 返回值
        """
        print(*args, **kwargs)
    
        return "返回值"
    
    
    my_func("装饰器被修复后,被装饰函数原函数的私有属性如__name__、__doc__就可以正常获取了,如下:")  # 执行原函数,实质是执行inner()
    print(my_func.__name__)  # 打印函数的名字
    print(my_func.__doc__)  # 打印函数的注释文档

      输出结果如下:

      

    7、带参数的装饰器 

      需求:很多函数共用一个装饰器,要求随时可以关闭装饰器功能,且尽可能的减少代码修改。 该需求可以用标记位来实现,如下:

    import time
    
    
    FLAG = True
    
    
    def timmer_out(flag):
        def timmer(func):
            def inner(*args, **kwargs):
                if flag:
                    start_time = time.time()
                    ret_func = func(*args, **kwargs)
                    end_time = time.time()
                    used_time = end_time - start_time
                    print("函数{name}执行时间:{time}".format(name=func.__name__, time=used_time))
                    # print("函数{name}执行时间:{time}".format_map({"name": func.__name__, "time": used_time}))
                else:
                    ret_func = func(*args, **kwargs)
    
                return ret_func
    
            return inner
    
        return timmer
    
    
    @timmer_out(FLAG)  # 第一步,先执行timmer_out(FLAG),得到返回值timmer。第二步执行@timmer,即 my_func1 = timmer(my_func1)
    def my_func1():
        time.sleep(1)
        print("my_func1")
    
    
    @timmer_out(FLAG)
    def my_func2():
        time.sleep(1)
        print("my_func2")
    
    
    @timmer_out(FLAG)
    def my_func3():
        time.sleep(1)
        print("my_func3")
    
    
    my_func1()
    my_func2()
    my_func3()

      当FLAG置为True时,装饰器功能生效,输出结果如下图所示:

      

      当FLAG置为False时,装饰器功能关闭,输出结果如下图所示:

      

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

     

    def wrapper1(func):
        def inner1():
            print("wrapper1, before func")
            func()
            print("wrapper1, after func")
    
        return inner1
    
    
    def wrapper2(func):
        def inner2():
            print("wrapper2, before func")
            func()
            print("wrapper2, after func")
    
        return inner2
    
    
    @wrapper2
    @wrapper1
    def my_func():
        print("function is my func")
    
    
    my_func()

      注意输出结果:

      

      为什么结果是这样?请看如下分析:

      

      为什么是先执行@wrapper1而不是@wrapeer2呢?因为装饰器在找到被装饰函数会优先执行。

  • 相关阅读:
    剑指offer51-正则表达式匹配
    剑指offer50-构建乘积数组
    剑指offer49-数组中的重复
    PHP系列笔记——Zend_Controller工作流程
    多态与重载
    读取文件数据的大概流程
    基于HTTP协议下载文件的实现
    C++中的面向对象笔记
    firebreath注册接口
    python读取excelxlsx,写入excel
  • 原文地址:https://www.cnblogs.com/Druidchen/p/7906705.html
Copyright © 2020-2023  润新知