python基础(8)-装饰器函数&进阶

从小例子进入装饰器

统计一个函数执行耗时

  • 原始版本
     1 import time
     2 
     3 # time模块有提供时间相关函数
     4 def do_something():
     5     print("do_something")
     6     time.sleep(0.5)  # 让程序停止0.5秒模拟其它操作耗时
     7 
     8 start = time.time()
     9 do_something()
    10 print(time.time() - start)
    11 #result:
    12 # do_something
    13 # 0.5000283718109131

    问题:上述代码可以完成这个功能,但之后会发现,如果我们要统计其它函数,就必须在每个函数前后加入相应代码

  • 装饰器版本1(无参数)
     1 import time
     2 
     3 def execute_time(func):
     4     def inner():
     5         start = time.time()
     6         func()
     7         print(time.time() - start)
     8 
     9     return inner
    10 
    11 # time模块有提供时间相关函数
    12 def do_something():
    13     print("do_something")
    14     time.sleep(0.5)  # 让程序停止0.5秒模拟其它操作耗时
    15 
    16 do_something = execute_time(do_something)
    17 do_something()
    18 #result:
    19 # do_something
    20 # 0.5000283718109131

    从上述代码可以看到,使用了另一个函数execute_time()给我们要统计耗时的函数进行了包装,这时,这个execute_time()函数就叫做装饰器函数,而我们要统计的那个函数也就是do_something()函数就是被装饰的函数.问题:函数执行的时候实际上是调用的execute_time()函数中的inner()函数,这种方法虽然解决了原始版本的问题,但是当我们要统计的函数拥有返回值的时候,这时候我们获取不到返回值.

  • 装饰器版本2(有固定参数)
    import time
    
    def execute_time(func):
        def inner(do):
            start = time.time()
            result = func(do)
            print(time.time() - start)
            return result
        return inner
    
    # time模块有提供时间相关函数
    def do_something(do):
        print("do_something", do)
        time.sleep(0.5)  # 让程序停止0.5秒模拟其它操作耗时
        return 'do_something over'
    
    do_something = execute_time(do_something)
    print(do_something('say hello'))
    # result:
    # do_something say hello
    # 0.5000283718109131
    # do_something over

    为了解决装饰器版本1的问题,我在inner()函数里面加了个返回值.问题:当被装饰函数的参数个数与inner()参数个数不同时,这个装饰器就不适用了

  • 装饰器版本3(动态参数)
     1 import time
     2 
     3 def execute_time(func):
     4     def inner(*args, **kwargs):
     5         start = time.time()
     6         result = func(*args, **kwargs)
     7         print(time.time() - start)
     8         return result
     9 
    10     return inner
    11 
    12 # time模块有提供时间相关函数
    13 def do_something(do1,do2):
    14     print("do_something", do1,do2)
    15     time.sleep(0.5)  # 让程序停止0.5秒模拟其它操作耗时
    16     return 'do_something over'
    17 
    18 do_something = execute_time(do_something)
    19 print(do_something('say hello1','say hello2'))
    20 # result:
    21 # do_something say hello1 say hello2
    22 # 0.5000283718109131
    23 # do_something over

    在第七天内容中有个知识点是动态参数,刚好可以解决这个问题

  • 终极版本(语法糖@)
     1 import time
     2 
     3 def execute_time(func):
     4     def inner(*args, **kwargs):
     5         start = time.time()
     6         result = func(*args, **kwargs)
     7         print(time.time() - start)
     8         return result
     9 
    10     return inner
    11 
    12 @execute_time
    13 def do_something(do1,do2):
    14     print("do_something", do1,do2)
    15     time.sleep(0.5)  # 让程序停止0.5秒模拟其它操作耗时
    16     return 'do_something over'
    17 
    18 # do_something = execute_time(do_something)
    19 print(do_something('say hello1','say hello2'))
    20 # result:
    21 # do_something say hello1 say hello2
    22 # 0.5000283718109131
    23 # do_something over

    对于装饰器,python内部给我们提供了语法糖支持.在需要被装饰的函数名上部使用[@装饰器函数名称]即可,简化上述代码18行

装饰器进阶

获取被包装函数原生属性

  • 例1:常规函数取函数名
    1 def func():
    2     print('执行中')
    3     print(func.__name__)
    4 
    5 func()
    6 # result:
    7 #   执行中
    8 #   func

    常规函数可以通过函数的__name__属性可拿到当前函数名称

  • 例2:被装饰函数取函数名
     1 def wrapper(func):
     2     def inner():
     3         print('执行前')
     4         result = func()
     5         print('执行后')
     6         return result
     7     return inner;
     8 @wrapper
     9 def func():
    10     print('执行中')
    11     print(func.__name__)
    12 
    13 func()
    14 # result:
    15 #   执行前
    16 #   执行中
    17 #   inner
    18 #   执行后

     问题:通过执行结果会发现,结果中想获取的函数名是func,而实际结果是inner.原因是@wrapper进行包装相当于执行一个操作:func=wrapper(func)=inner

  • 解决

    使用functools模块

     1 from functools import wraps
     2 
     3 def wrapper(func):
     4     @wraps(func)
     5     def inner():
     6         print('执行前')
     7         result = func()
     8         print('执行后')
     9         return result
    10 
    11     return inner;
    12 
    13 @wrapper
    14 def func():
    15     print('执行中')
    16     print(func.__name__)
    17 
    18 func()
    19 # result:
    20 #   执行前
    21 #   执行中
    22 #   func
    23 #   执行后

    导入functools模块后通过第4行操作,会发现执行的函数即使被包装但还是能获取到它本身的属性

带参数的装饰器

  • 例1
     1 def wrapper(func):
     2     def inner():
     3         print('执行前')
     4         result = func()
     5         print('执行后')
     6         return result
     7 
     8     return inner;
     9 
    10 @wrapper
    11 def func_1():
    12     print('执行中')
    13 
    14 @wrapper
    15 def func_2():
    16     print('执行中')
    17 
    18 @wrapper
    19 def func_3():
    20     print('执行中')
    21 ...
    22 @wrapper
    23 def func_n():
    24     print('执行中')

    问题:通过上述代码会发现,有很多函数都用了同一个装饰器,如果有一天要取消这些函数上的装饰,就必须对每一个函数进行修改

  • 解决

    定义一个全局变量flag,并给原本装饰器外部再加一层函数用来接收参数,inner()函数内部通过外部参数flag判断被装饰函数的执行与否,修改flag即可控制装饰器的执行结果,如下:

     1 from functools import wraps
     2 flag = True
     3 
     4 def wrapper_out(flag):
     5     def wrapper(func):
     6         @wraps(func)
     7         def inner():
     8             if (flag):
     9                 print('{}执行前'.format(func.__name__))
    10                 result = func()
    11                 print('{}执行后'.format(func.__name__))
    12             else:
    13                 result = func()
    14             return result
    15 
    16         return inner
    17 
    18     return wrapper
    19 
    20 @wrapper_out(flag)
    21 def func_1():
    22     print('{}执行中'.format(func_1.__name__))
    23 
    24 @wrapper_out(flag)
    25 def func_2():
    26     print('{}执行中'.format(func_2.__name__))
    27 
    28 @wrapper_out(flag)
    29 def func_3():
    30     print('{}执行中'.format(func_3.__name__))
    31 
    32 ...
    33 
    34 @wrapper_out(flag)
    35 def func_n():
    36     print('{}执行中'.format(func_n.__name__))
    37 
    38 func_1()
    39 func_2()
    40 func_3()
    41 func_n()
    42 
    43 #result:
    44     # func_1执行前
    45     # func_1执行中
    46     # func_1执行后
    47     # func_2执行前
    48     # func_2执行中
    49     # func_2执行后
    50     # func_3执行前
    51     # func_3执行中
    52     # func_3执行后
    53     # func_n执行前
    54     # func_n执行中
    55     # func_n执行后
    from functools import wraps
    flag = False
    
    def wrapper_out(flag):
        def wrapper(func):
            @wraps(func)
            def inner():
                if (flag):
                    print('{}执行前'.format(func.__name__))
                    result = func()
                    print('{}执行后'.format(func.__name__))
                else:
                    result = func()
                return result
    
            return inner
    
        return wrapper
    
    @wrapper_out(flag)
    def func_1():
        print('{}执行中'.format(func_1.__name__))
    
    @wrapper_out(flag)
    def func_2():
        print('{}执行中'.format(func_2.__name__))
    
    @wrapper_out(flag)
    def func_3():
        print('{}执行中'.format(func_3.__name__))
    
    ...
    
    @wrapper_out(flag)
    def func_n():
        print('{}执行中'.format(func_n.__name__))
    
    func_1()
    func_2()
    func_3()
    func_n()
    
    #result:
        # func_1执行中
        # func_2执行中
        # func_3执行中
        # func_n执行中

多个装饰器装饰同一个函数

  • 代码
     1 def wrapper1(func):
     2     def inner():
     3         print('wrapper1 ,before func')
     4         func()
     5         print('wrapper1 ,after func')
     6 
     7     return inner
     8 
     9 def wrapper2(func):
    10     def inner():
    11         print('wrapper2 ,before func')
    12         func()
    13         print('wrapper2 ,after func')
    14 
    15     return inner
    16 
    17 @wrapper1
    18 @wrapper2
    19 def f():
    20     print('in f')
    21 
    22 f()
    23 # result:
    24 # wrapper1 ,before func
    25 # wrapper2 ,before func
    26 # in f
    27 # wrapper2 ,after func
    28 # wrapper1 ,after func
  • 图解

    从上图可以看到,从1-9步是装饰器的装载过程,10-18步是执行过程.结论:多个装饰器装饰同一个函数时,装载顺序是从下到上,但执行顺序却是从上到下,可以理解为创建了一个装饰器栈(先进后出)

转载于:https://www.cnblogs.com/zze46/p/9549737.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值