• Python基础之装饰器


    一、什么是装饰器?

    装饰器本质上就是一个Python函数,它可以让其它函数在不需要做任何代码变动的前提下,增加额外的功能,装饰器的返回值也是一个函数对象。

    装饰器的应用场景:比如插入日志,性能测试,事物处理,缓存等等场景。

    二、装饰器的形成过程

    现在我有一个需求,我想让你在不改变函数代码的情况下,测试出这个函数的执行时间:

    import time
    
    
    def func1():
        print("in func1")
    
    
    def timer(func):
        def inner():
            start = time.time()
            func()
            print(time.time() - start)
        return inner
    
    
    func1 = timer(func1)
    func1()
    

    但是如果有多个函数,我都想让你测试他们的执行时间,你岂不是每个函数都要写一遍func1 = timer(func1)?

    这样写非常麻烦,因为这些函数的函数名都是不相同的,有func1、func2,func3等等。

    针对这种情况,python给我们提供了一个简单快捷的方法,那就是语法糖。

     1 import time
     2 
     3 
     4 def timer(func):
     5     def inner():
     6         start = time.time()
     7         func()
     8         print(time.time() - start)
     9     return inner
    10 
    11 
    12 @timer  # ==> func1 = timer(func1)
    13 def func1():
    14     print("in func1")
    15     
    16     
    17 func1()
    装饰器 ——语法糖

    刚刚我们讨论的装饰器都是装饰不带参数的函数,现在要装饰一个带参数的函数要怎么办呢?

     1 import time
     2 
     3 def timer(func):
     4     def inner(a):
     5         start = time.time()
     6         func(a)
     7         print(time.time() - start)
     8     return inner
     9 
    10 
    11 @timer
    12 def func1(a):
    13     print(a)
    14 
    15 func1(1)
    装饰器——带参数的装饰器
     1 import time
     2 
     3 
     4 def timer(func):
     5     def inner(*args, **kwargs):
     6         start = time.time()
     7         result = func(*args, **kwargs)
     8         print(time.time() - start)
     9         return result
    10     return inner
    11 
    12 
    13 @timer #==> func1 = timer(func1)
    14 def func1(a, b):
    15     print("in func1")
    16     
    17 
    18 @timer #==> func2 = timer(func2)
    19 def func2(a):
    20     print("in func2 and get a:%s" % a)
    21     return "func2 end"
    22 
    23 
    24 func1("aaa", "bbb")
    25 print(func2("aa"))
    装饰器——hold住所有参数的装饰器

    上面的装饰器已经非常完美了,但是我们在正常情况下查看函数信息的方法却在此处全部失效了:

    def index():
        """
        这是一个主页信息
        :return:
        """
        print("from index")
    
    
    print(index.__doc__)    # 查看函数注释的方法
    print(index.__name__)   # 查看函数名的方法
    

    那么如何解决这个问题呢?

    from functools import wraps
    
    
    def deco(func):
        @wraps(func)    # 放在最内层函数最上方
        def wrapper(*args, **kwargs):
            return func(*args, **kwargs)
        return wrapper
    
    @deco
    def index():
        """
        显示首页信息
        :return:
        """
        print("from index")
    
    
    print(index.__doc__)
    print(index.__name__)

    三、开放封闭原则

    装饰器是完美的遵循了这个开放封闭原则的。

    那么什么是开放封闭原则呢?

    我们可以从下面两个方面来看。

    1. 对扩展是开放的

    为什么要对扩展开放呢?

    我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能,并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。

    2. 对修改是封闭的

    为什么要对修改封闭呢?

    就像我们刚刚提到的,因为我们写的一个函数,很有可能已经交付给其他人使用了,如果这个我们对其进行了修改,很有可能影响其他已经在使用该函数的用户。

    四、装饰器的主要功能和固定结构

    def timer(func):
        def inner(*args, **kwargs):
            """执行函数之前要做的"""
            result = func(*args, **kwargs)
            """执行函数之后要做的"""
            return result
        return inner
    装饰器的固定格式——普通版
    1 from functools import wraps
    2 
    3 def deco(func):
    4     @wraps(func)    # 加在最内层函数最上方
    5     def wrapper(*args, **kwargs):
    6         return func(*args, **kwargs)
    7     return wrapper
    装饰器的固定格式——wrapper版

    五、带参数的装饰器

    假如你有成千上万个函数使用了一个装饰器,现在你想把这些装饰器都取消掉,你要怎么做?

    如果一个一个的取消,那任务量也太大了吧。

    万一,没过几天,你又需要用这些装饰器,岂不是要吐血。

    那么解决办法,就是在装饰器上加上参数:

     1 def wrapper_out(flag):
     2     def wrapper(func):
     3         @wraps(func)
     4         def inner(*args, **kwargs):
     5             if flag:
     6                 print("执行函数之前要做的")
     7 
     8             result = func(*args, **kwargs)
     9 
    10             if flag:
    11                 print("执行函数之后要做的")
    12 
    13             return result
    14         return inner
    15     return wrapper
    16 
    17 
    18 @wrapper_out(False)     # 通过传递True和False来控制装饰器内部的运行效果
    19 def func():
    20     pass
    21 
    22 
    23 func()
    带参数的装饰器

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

    先执行下面这样一个代码

     1 def wrapper1(func):
     2     def inner(*args, **kwargs):
     3         print("111")
     4         result = func(*args, **kwargs)
     5         print("222")
     6         return result
     7     return inner
     8 
     9 
    10 def wrapper2(func):
    11     def inner(*args, **kwargs):
    12         print("333")
    13         result = func(*args, **kwargs)
    14         print("444")
    15         return result
    16     return inner
    17 
    18 
    19 @wrapper2
    20 @wrapper1
    21 def func():
    22     print("this is func")
    23 
    24 func()
    多个装饰器装饰一个函数

    当执行完毕后,可以看到执行结果为:

    333
    111
    this is func
    222
    444

    执行顺序:首先@warpper1装饰器来,然后获取到一个新函数是wrapper1中的inner,然后执行@wrapper2。这个时候,wrapper2装饰的就是wrapper1中的inner了。

    所以,执行顺序就像:第二层装饰器前(第一层装饰器前(目标)第一层装饰器后)第二层装饰器后。程序从左到右执行起来,这就是我们看到的结果。

  • 相关阅读:
    angular 按下回车键触发事件
    vue 父组件与子组件的通信
    最近在开发一个文章聚合的工具
    Martinjingyu的开发环境
    个推push数据统计(爬虫)
    基于redis的订单号生成方案
    电商平台--Mysql主从搭建(2)
    Mysql主从搭建(1)
    mysql物理级别热备脚本
    外键查询及删除
  • 原文地址:https://www.cnblogs.com/yang-wei/p/10022998.html
Copyright © 2020-2023  润新知