Python带参装饰器

发布时间:2019-08-06 13:56:23编辑:auto阅读(1392)

    装饰器(无参)

      它是一个函数;

      函数作为它的形参;

      返回值也是一个函数;

      可以使用@functionname方式,简化调用;

    装饰器和高阶函数

      装饰器是高阶函数,但装饰器是对传入函数的功能的装饰(功能增强)

    import datetime
    import time
    
    def logger(fn):
        def wrap(*args, **kwargs):
            #before 功能增强
            print("args={},kwargs={}".format(args, kwargs))
            start = datetime.datetime.now()
            ret = fn(*args, **kwargs)
            #after 功能增强
            duration = datetime.datetime.now() - start
            print("function {} took {}s.".format(fn.__name__, duration.total_seconds()))
            return ret
        return wrap
    
    @logger
    def add(x, y):
        print("======call add======")
        time.sleep(2)
        return x + y
    
    print(add(4, y=5))


    讲一个新的小知识点---文档字符串

    python的文档

      python是文档字符串Documentation Strings

      在函数语句块的第一行,且习惯是多行的文本,所以多使用三引号;

      惯例是首字母大写,第一行写概述,空一行,第三行写详细描述;

      可以使用特殊属性__doc__访问这个文档

    def add(x, y):
        """This is a function of addition"""
    
        a = x + y
        return x + y
    
    print("name={}\ndoc={}".format(add.__name__, add.__doc__))
    print(help(add))

    这就是文档字符串,通过文档字符串可以查看这个函数的帮助等一些信息



    我们在来看一段代码,它的输出结果是什么呢?

    import datetime
    import time
    
    def logger(fn):
        def wrap(*args, **kwargs):
            """This is a wrapper"""
            #before 功能增强
            print("args={},kwargs={}".format(args, kwargs))
            start = datetime.datetime.now()
            ret = fn(*args, **kwargs)
            #after 功能增强
            duration = datetime.datetime.now() - start
            print("function {} took {}s.".format(fn.__name__, duration.total_seconds()))
            return ret
        return wrap
    
    @logger
    def add(x, y):
        """This is a function"""
        print("======call add======")
        time.sleep(2)
        return x + y
    
    # print(add(4, y=5))
    
    print(add.__name__, add.__doc__,sep='\n')

    运行结果如下:

    wrap

    This is a wrapper

    通过代码也能看出来,使用装饰器是有副作用的:

      原函数对象的属性都被替换了,而使用装饰器,我们的需求是查看被封装函数的属性,如何解决?

    blob.png


    想一下,这个函数的调用为什么要写到79行???插入到其它行,行不行?这个自己考虑想想吧,这里就不提了。


    既然我们学会了装饰器,那如何把copy_properties改造成装饰器?这就引出了我们的带参装饰器

    import datetime
    import time
    
    def copy_properties(src):
        def wrapper(dst):
            dst.__name__ = src.__name__
            dst.__doc__ = src.__doc__
            dst.__qualname__ = src.__qualname__
            return dst
        return wrapper
    
    def logger(fn):
        @copy_properties(fn)# copy_properties.wrapper(logger.wrap),
        def wrap(*args, **kwargs):
            """This is a wrapper"""
            #before 功能增强
            print("args={},kwargs={}".format(args, kwargs))
            start = datetime.datetime.now()
            ret = fn(*args, **kwargs)
            #after 功能增强
            duration = datetime.datetime.now() - start
            print("function {} took {}s.".format(fn.__name__, duration.total_seconds()))
            return ret
        return wrap
    
    @logger
    def add(x, y):
        """This is a function"""
        print("======call add======")
        time.sleep(2)
        return x + y
    
    # print(add(4, y=5))
    
    print(add.__name__, add.__doc__, add.__qualname__, sep='\n')

    通过copy_properties函数将包装函数的属性覆盖掉包包装函数;

    凡是被装饰的函数都需要复制这些属性,这个函数很通用;

    可以将复制属性的函数构建成装饰器函数,带参装饰器;


    需求:获取函数的执行时长,对时长超过阈值的函数记录一下:

    import datetime
    import time
    
    def logger(t):# def logger(t1, t2, t3....tn):
        def _logger(fn):
            #@copy_properties(fn) 可以把上面写的复制属性的函数装饰在此
            def wrap(*args, **kwargs):
                #before 功能增强
                # print("args={},kwargs={}".format(args, kwargs))
                start = datetime.datetime.now()
                ret = fn(*args, **kwargs)
                #after 功能增强
                duration = (datetime.datetime.now() - start).total_seconds()
                if duration > t:
                    print("function {} took {}s.".format(fn.__name__, duration))
                return ret
            return wrap
        return _logger
    
    @logger(3)# add = logger(3)(add), @logger(3, 5, 9,...n)
    def add(x, y):
        print("======call add======")
        time.sleep(5)
        return x + y
    
    print(add(4, y=5))

    装饰器(带参)

      它是一个函数;

      函数作为它的形参;

      返回值是一个不带参的装饰器函数;

      使用@functionname(参数列表)方式调用;

      可以看做在装饰器外层又加了一层函数;



    将记录的功能提取出来,这样就可以通过外部提供的函数来灵活的控制输出:

    def logger(duration, func=lambda name, duration: print('{} took {}s'.format(name, duration))):
        def _logger(fn):
            @copy_properties(fn):
            def wrapper(*args, **kwargs):
                start = datetime.datetime.now()
                ret = fn(*args, **kwargs)
                delta = (datetime.datetime.now() - start).total_seconds()
                if delta > duration:
                    func(fn.__name__, duration)
                return ret
            return wrapper
        return _logger


关键字