• python 装饰器


    1 为什么要用装饰器

    在介绍装饰器以前,我们先介绍一个写代码需要遵循的原则:开发封闭原则(OCP,Open Closed Principle)。一但产品上线,就尽量避免修改源代码。但在后期,也会添加各种需求。所以在设计之初,需要预留一些接口,用来作修改的手段。装饰器也应运而生。
    开发封闭原则:已经实现的功能代码不允许被修改,但可以被扩展已经实现的功能代码不允许被修改,但可以被扩展。
    封闭:已实现的功能代码块
    开放:对扩展开发


    # 2 装饰器的作用 装饰器(decorator)的作用,**在不修改源代码以及调用方式的情况下,给源码加入一些新功能。**

    3 哪些能作装饰器

    任何可调用的对象都可以做装饰器,如函数、类。
    那被装饰的对象又有哪些要求?
    和装饰器一样,只要是可调用的对象,也都可以做被装饰对象。

    4 装饰器有哪几种

    先从简单的开始讲,不适用装饰器是如何实现的:
    我们都知道在python中函数是第一类对象,可以作为参数,也可以赋值给其他变量。

    import time
    def log(func):
        def swapper():
            start=time.clock()
            func()
            end=time.clock()
            print("run time is:",end-start)
        return swapper
    
    def index():
        print("this is index")
    
    index=log(index)
    index()
    
    result:
    this is index
    run time is: 2.285910487477409e-05
    

    1 无参装饰器

    上面的代码我们使用装饰器,就变成:

    import time
    def log(func):  #1 #3
        def swapper(): #4
            #print(func.__name__)    #index
            start=time.clock()   #8
            func()      #9  #11
            end=time.clock()   #12
            print("run time is:",end-start)  #13
        return swapper  #5
    
    @log   #index=log(index)  #2  #6
    def index():
        #print(index.__name__)    #swapper
        print("this is index")   #10
    
    index()   #7  #14
    

        不难看出,@log等价于index=log(index)。其实@log的定义就是,将@正下方的函数名作为log()的参数,并作为接受返回值的对象。所以遇到@log,就把它理解为index=log(index),当执行到#5时,return swapper,也就是index=swapper,所以在第#9执行原index(),index.__name__打印的是swapper。而在第#4,形参func实际上接收的是index,所以func.__name__打印的是index。

    我们再来看另一种情况,如果在index(),添加形参怎么做:

    import time
    def log(func):
        def swapper(arg):
            #print(func.__name__)
            start=time.clock()
            func(arg)
            end=time.clock()
            print("run time is:",end-start)
        return swapper
    
    @log
    def index(name):
        print("this is %s's index"%name)
    
    index("abc")
    
    result:
    this is abc's index
    run time is: 2.7057715974222393e-05
    

        如果要接收任意参数,把swapper(arg)改为swapper(*args,**args),func(arg)改为func(*args,**args)

    2 有参装饰器

    要给内部函数传递一个或多个变量,能想到的一个闭包,一个定义全局命名空间的变量。同时又希望函数能携带这一个或多个变量的信息,只能是闭包。所以我们只需要在已有的装饰器外面再包一层函数,并把这一个或多个变量作为形参。这样内部函数就能获取到了。

    import time
    def log2(type):
        def log(func):
            def swapper(*args,**kwargs):
                if type=="file":
                    start=time.clock()
                    res=func(*args,**kwargs)
                    end=time.clock()
                    print("run time is :",end-start)
                    return res
                elif type=="sql":
                    print("other")
            return swapper
        return log
    
    @log2(type="file") #log #home=log(home)
    def home(name):   #home=log(home)
        print("%s 's personal page!"%name)
    
    home("aaa")
    
    result:
    aaa 's personal page!
    run time is : 2.7057715974222393e-05
    

    3 补充

    我们从“1 无参装饰器”种不难看出func.__name__和index.__name__的值并不一样。

    import time
    
    def log(func):
        def swapper(*args,**kwargs):
            print("function swapper:",func.__name__)
            start=time.clock()
            res=func(*args,**kwargs)
            end=time.clock()
            print("run time is :",end-start)
            return res
        return swapper
    
    @log
    def now():
        print("function now:",now.__name__)
        print("2017-4-10")
    
    now()
    
    result:
    function swapper: now
    function now: swapper
    2017-4-10
    run time is : 1.679444439779321e-05
    
    

    加入functools.warps(func)

    import functools
    import time
    
    def log(func):
        @functools.wraps(func)
        def swapper(*args,**kwargs):
            print("function swapper:",func.__name__)
            start=time.clock()
            res=func(*args,**kwargs)
            end=time.clock()
            print("run time is :",end-start)
            return res
        return swapper
    
    @log
    def now():
        print("function now:",now.__name__)
        print("2017-4-10")
    
    now()
    result:
    function swapper: now
    function now: now
    2017-4-10
    run time is : 1.77274690865595e-05
    

    @functools.wraps(func)也是一个装饰器,作用是,把原始函数的__name__等属性复制到 它所修饰的正下方的函数wrapper()中。除了函数名,还有__module__、__doc__等属性。

    如何解除装饰器

    from functools import wraps
    import time
    
    def log(func):
        @wraps(func)
        def swapper(*args,**kwargs):
            print("function swapper:",func.__name__)
            start=time.clock()
            res=func(*args,**kwargs)
            end=time.clock()
            print("run time is :",end-start)
            return res
        return swapper
    
    @log
    def now():
        print("function now:",now.__name__)
        print("2017-4-10")
    
    now()
    print("-----")
    now.__wrapped__()
    

    执行结果:

    function swapper: now
    function now: now
    2017-4-10
    run time is : 3.032331653110589e-05
    -----
    function now: now
    2017-4-10
    

    若装饰器是通过@wraps来实现的,就可以通过__wrapper__属性来访问原始函数。
    最后要说的是,并不是所有的装饰器都使用了 @wraps ,因此这里的方案并不全部适用。特别的,内置的装饰器 @staticmethod 和 @classmethod 就没有遵循这个约定 (它们把原始函数存储在属性 func 中)。

  • 相关阅读:
    MySQL 常见问题
    Gym 101652P:Fear Factoring 数论
    万网域名查询接口(API)的说明
    在ashx页面获取Session值
    asp.net 操作xml
    用SQL进行嵌套查询
    在IE下 javascript比较两个字符串(包括中文汉字)是否相等
    asp.net中protected/private/public的用法与区别
    第一个
    解决IDEA Unable to save settings: Failed to save settings. Please restart IntelliJ IDEA问题的一种方法。
  • 原文地址:https://www.cnblogs.com/yangzhenwei123/p/6759205.html
Copyright © 2020-2023  润新知