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

    装饰器介绍

    啥是装饰器呀?

    答:给函数对象增加新功能的一个工具。装饰器的内部函数其实就是一个闭包函数。

    装饰器有啥原则呀?

    答:利用装饰器增加新功能时,不修改被装饰对象的源代码,不改变被装饰对象的调用方式。

    简单装饰器示例

    有一个函数:
    def func():
        print('我是一个函数')
    
    给这个函数加个功能,计算该函数运行时间。给一个函数加功能,不改变函数源代码,不改变函数调用方式,使用装饰器!
    import time
    def count_time(func):
        def wrapper():
            start = time.time()
            time.sleep(2)
            func()
            print('函数执行消耗的时间为:%s' % (time.time()-start))
        return wrapper
    
    func = count_time(func)   #本质上是返回wrapper的函数。
    
    func()   #本质上调用的是 wrapper(), wrapper里面的func()才能调用的原func函数。

    装饰器语法糖:为了直观,python 为了省略 func = count_time(func)这一步,提出了语法糖的概念。

    import time
    def count_time(func):
        def wrapper():
            start = time.time()
            time.sleep(2)
            func()
            print('函数执行消耗的时间为:%s' % (time.time()-start))
        return wrapper
    
    @count_time  #重点:这句话等同于: func = count_time(func)
    def func():
        print('我是一个函数')
    
    func()   #本质上调用的还是count_time(func)返回的 wrapper(), wrapper里面的func()才能调用的原func函数。

    当被装饰器有参数和返回值的时候该怎么办?

    这里介绍一下牛逼的装饰器模板,直接用就完事了。推导过程就不写了,累了,等我想起来了再说吧...(ps: 虽然模板是抄的,但程序员嘛,程序员抄东西,能叫抄吗?不能,这叫借鉴。)

    def outter(func):
        def wrapper(*args,**kwargs):
            print('执行被装饰函数之前 你可以做的操作')
            res = func(*args,**kwargs)
            print('执行被装饰函数之后 你可以做的操作')
            return res
        return inner
    
    @outter  # 恒等于 func = outter(func) 返回wrapper这个闭包函数。
    def func(a, b, c, d, e, f)
        print(a, b, c, d, e, f)
        return '我是一个帅比'
        
    #这里调用的时候
    func(1,2,3,4,5,6)  #就相当于 调用wrapper(1, 2, 3, 4, 5, 6)  

    多层装饰器

    装饰器在装饰的时候 顺序从下往上。如所示:装饰的时候先调用 outtter3(), 再outtter2(), 在outtter3()
    装饰器在执行的时候 顺序从上往下。

    那么,装饰器什么时候执行呢?就是被装饰函数被调用的时候。如下,调用index,index(), 然后执行多层装饰器时,会先调用最外面的,也就是 outter1(), outter2(), 最后outter3()

    def outter1(func1):
        print('加载了outter1')
        def wrapper1(*args,**kwargs):
            print('执行了wrapper1')
            res1=func1(*args,**kwargs)
            return res1
        return wrapper1
    def outter2(func2):
        print('加载了outter2')
        def wrapper2(*args,**kwargs):
            print('执行了wrapper2')
            res2=func2(*args,**kwargs)
            return res2
        return wrapper2
    def outter3(func3):
        print('加载了outter3')
        def wrapper3(*args,**kwargs):
            print('执行了wrapper3')
            res3=func3(*args,**kwargs)
            return res3
        return wrapper3

    @outter1
    # index = outter1(wapper2) @outter2 # wrapper2 = outter2(wrapper3) @outter3 # wrapper3 = outter3(最原始的index函数内存地址) def index(): print('from index') index() >>>加载了outter3 >>>加载了outter2 >>>加载了outter1 >>>执行了wrapper1 >>>执行了wrapper2 >>>执行了wrapper3 >>>from index

    再来一个例子:

    def wrapper1(func):
        def inner():
            print('wrapper1 ,before func')
            func()
            print('wrapper1 ,after func')
        return inner
    
    def wrapper2(func):
        def inner():
            print('wrapper2 ,before func')
            func()
            print('wrapper2 ,after func')
        return inner
    
    @wrapper2
    @wrapper1
    def f():
        print('in f')
    
    f()
    
    >>>wrapper2 ,before func
    >>>wrapper1 ,before func
    >>>in f
    >>>wrapper1 ,after func
    >>>wrapper2 ,after func

    #注意,这个例子的wrapper,只有被装饰器执行调用的时候才被调用。

     装饰器传参

    当想给装饰器传个参数的时候,我们知道,传参数的方式有两种:1、普通函数传参。2、闭包传参。

    如果我们给装饰器后面写个括号,传参数,装饰器不就被调用了,况且装饰器里面的参数只能有一个,那就是被装饰的那个函数。

    所以这时候,我们选择闭包传参。怎么闭包传参呢?

    在装饰器外面再套一层函数不就OK了。如下所示:

    def outer_two(a, b, c):
        def outer(func):
            def wrapper(*args, **kwargs):
                
                if a == 1:
                    res = func(*args, **kwargs)
                    return res
                else:
                    print('如果a不等于1执行的操作')
            
            return wrapper
    
        return outer
            
            
    @outer_two(1, 2, 3)   #这个就等于 outer, 因为outer_tow调用时的返回值时outer
    def func():
        print('被装饰器')    

     装饰器补充点

    由来:使用装饰器有一个小毛病,就是想要查看被装饰器的注释或者名字时,比如使用如下方法时:

    print(help(index)) # 查看函数的注释
    print(index.__name__) # 查看函数名字符串形式

    结果看到的注释不是被装饰器的注释,而是装饰器里面的内部闭包函数的注释。

    查看到的函数名字也是装饰器内部函数的名字,这样我就不爽了,我调用函数的时候,想看函数的名字和注释,看到的却不是对的。

    基于以上情况,Python提供了解决方案,叫做装饰器修复技术。

    from functools import wraps
    def outter(func):
        @wraps(func)  # 装饰器修复技术
        def inner(*args,**kwargs):
            """
            我是inner函数
            :param args:
            :param kwargs:
            :return:
            """
            print('执行被装饰函数之前 你可以执行的操作')
            res = func(*args,**kwargs)
            print('执行被装饰函数之后 你可以执行的操作')
            return res
        return inner
    
    @outter  # index = outter(最原始的index内存地址)
    def index():
        """
        这是index函数
        :return:
        """
        pass
    
    #注意wraps使用的位置!

    这时候:

    用户查看被装饰函数的函数名的时候查看到的就是被装饰函数本身
    用户查看被装饰函数的注释的时候查看到的就是被装饰函数的注释

  • 相关阅读:
    安卓学习-界面-ui-GirdView
    安卓学习-界面-ui-AutoCompleteTextView
    安卓学习-界面-ui-Adapter
    安卓学习-界面-ui-ListView
    安卓学习-界面-ui-ImageView
    安卓学习-界面-ui-AnalogClock、DigitalClock和Chronometer
    clientdataset生成sql
    安卓学习-界面-ui-ToggleButton Switch
    DOM通过ID或NAME获取值
    DOM基本代码二
  • 原文地址:https://www.cnblogs.com/KbMan/p/11173697.html
Copyright © 2011-2022 走看看