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

    需求

    • 一个加法函数,想增强它的功能,能够输出被调用过以及调用过参数的信息
    def add1(x,y):
        print("call add, x+y") #日志输出的控制台
        return x+y

    以上函数完成了需求,但是存在以下缺点:

    • 打印语法的耦合太高
    • 加法函数属于业务功能,而输出信息的功能,属于非业务功能代码,不该放在业务函数加法中

    业务功能分离

    def add1(x,y):
        return x+y
    def logger(fn):
        print("begin")
        x = fn(4,5)
        print("end")
        return x
    print(logger(add1))

    但这里fn调用传参是个问题

    解决传参的问题

    def add1(x,y):
        return x+y
    def logger(fn,*args,**kwargs):
        print("begin")
        x = fn(*args,**kwargs) #这里是在解构
        print("end")
        return x
    print(logger(add1,5,y=10))

    柯里化

    def add(x,y):
        return x+y
    def logger(fn):
        def wrapper(*args,**kwargs):
            print("begin")
            x = fn(*args,**kwargs) #这里是在解构
            print("end")
            return x
        return wrapper
    
    
    print(logger(add)(5,y = 10))

    装饰器语法糖

    def logger(fn):
        def wrapper(*args,**kwargs):
            print("begin")
            x = fn(*args,**kwargs) #这里是在解构
            print("end")
            return x
        return wrapper
    @logger
    def add(x,y):
        return x+y
    
    print(add(45,500))

    @logger是装饰器的语法

    装饰器(无惨)

    • 是一个函数
    • 函数作为它的参数
    • 返回值也是一个函数
    • 可以使用@functionname方式,简化调用

    装饰器和高阶函数

    • 装饰器是高阶函数,但装饰器是对传入函数的功能是装饰(增强)
    import datetime
    import time
    def logger(fn):
        def wrapper(*args,**kwargs):
            print("begin")
            print("args={},kwargs={}".format(args,kwargs))
            start = datetime.datetime.now()
            x = fn(*args,**kwargs) #这里是在解构
            duration = datetime.datetime.now() -start
            print("function {} took {}s.".format(fn.__name__,duration.total_seconds()))
            return x
        return wrapper
    @logger # add = logger(add)
    def add(x,y):
        print("==========call add==========")
        time.sleep(2)
        return x+y
    
    print(add(45,500))

    Python 文档字符串

    python文档

    • python文档字符串Documentation Strings
    • 在函数语句块的第一行,且习惯是多行的文本,所以多引用三引号
    • 惯例是首字符大写,第一行写概述,空一行,第三行写详细描述
    • 可以使用特殊属性__doc__访问该文档
    def add(x,y):
        """This is function of addition"""
        a = x+y
        return x+y
    print("name={}
    doc={}".format(add.__name__,add.__doc__))
    print(help(add))

    副作用出现的问题:

    def logger(fn):
        def wrapper(*args,**kwargs):
            "I am a wrapper"
            print("begin")
            x = fn(*args,**kwargs) #这里是在解构
            print("end")
            return x
        return wrapper
    @logger #add= logger(add)
    def add(x,y):
        "This is a function for add"
        return x+y
    print("name={},doc={}".format(add.__name__,add.__doc__))

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

    提供一个函数,被封装函数属性==copy==>包装函数属性

    def copy_properties(src,dest): #可以改造成装饰器
        dest.__name__ = src.__name__
        dest.__doc__ = src.__doc__
    
    def logger(fn):
        def wrapper(*args,**kwargs):
            "I am a wrapper"
            print("begin")
            x = fn(*args,**kwargs) #这里是在解构
            print("end")
            return x
        copy_properties(fn,wrapper)
        return wrapper
    @logger #add= logger(add)
    def add(x,y):
        "This is a function for add"
        return x+y
    print("name={},doc={}".format(add.__name__,add.__doc__))
    • 通过copy_properties函数将被包装函数的属性覆盖掉包装函数
    • 凡是被装饰的函数都需要复制这些属性,这个函数很通用
    • 可以将复制属性的函数构建成装饰器函数,带参装饰器

    改造成带参装饰器

    def copy_properties(src):#可以改造成装饰器
        def _copy_properties(dest):
            dest.__name__ = src.__name__
            dest.__doc__ = src.__doc__
            return dest
        return _copy_properties
    def logger(fn):
        @copy_properties(fn) #wrapper = copy_properties(fn)(wrapper)
        def wrapper(*args,**kwargs):
            "I am a wrapper"
            print("begin")
            x = fn(*args,**kwargs) #这里是在解构
            print("end")
            return x
        return wrapper
    @logger #add= logger(add)
    def add(x,y):
        "This is a function for add"
        return x+y
    print("name={},doc={}".format(add.__name__,add.__doc__))

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

    import datetime
    import time
    def copy_properties(src):#可以改造成装饰器
        def _copy_properties(dest):
            dest.__name__ = src.__name__
            dest.__doc__ = src.__doc__
            return dest
        return _copy_properties
    def loger(duration):
        def _logger(fn):
            @copy_properties(fn)
            def wrapper(*args,**kwargs):
                start = datetime.datetime.now()
                x = fn(*args, **kwargs)
                delta=(datetime.datetime.now()-start).total_seconds()
                print('so slow') if delta > duration else print('so fast')
                return x
            return wrapper
        return _logger
    @loger(5)
    def add(x,y):
        time.sleep(6)
        return x + y
    print(add(10,24))

    带参装饰器:

    • 它是一个函数
    • 函数作为它的形参
    • 返回值是一个不带参的装饰器函数
    • 使用@functionname(参数列表)方式调用
    • 可以看做在装饰器外层又加了一层函数

    打印功能提出来

    import datetime
    import time
    def copy_properties(src):#可以改造成装饰器 add
        def _copy_properties(dest): #wrapper
            dest.__name__ = src.__name__
            dest.__doc__ = src.__doc__
            return dest
        return _copy_properties
    def loger(duration,func=lambda name,duration:print('{} took {}s'.format(name,duration))):  #loger(5)(add)  duration=5
        def _logger(fn):  #loger(5)(add) fn = add
            @copy_properties(fn) #wrapper = _copy_properties(fn)(wrapper)
            def wrapper(*args,**kwargs):
                start = datetime.datetime.now()
                x = fn(*args, **kwargs)
                delta=(datetime.datetime.now()-start).total_seconds()
                if delta > duration:
                    func(fn.__name__,duration)
                return x
            return wrapper
        return _logger
    @loger(5) #add = loger(5)(add)
    def add(x,y):
        time.sleep(6)
        return x + y
    print(add(10,24))

    functools模块

    functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)

    • 类似copy_properties功能
    • wrapper 包装函数,被更新者,wrapped 被包装函数,数据源
    • 元组WRAPPER_ASSIGNMENTS中是要被覆盖的属性,'__module__', '__name__', '__qualname__', '__doc__', '__annotations__'  模块名、名称、限定名、文档、参数注解
    • 元组WRAPPER_UPDATES中是要被更新的属性,__dict__属性字典
    • 增加一个__warpped__属性,保留着warpped函数
    import datetime, time, functools
    def logger(duration, func=lambda name, duration: print('{} took {}s'.format(name, duration))):
        def _logger(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 functools.update_wrapper(wrapper, fn)
        return _logger
    @logger(5) # add = logger(5)(add)
    def add(x,y):
        time.sleep(1)
        return x + y
    
    print(add(5, 6), add.__name__, add.__wrapped__, add.__dict__, sep='
    ')

    @functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)

    • 类似copy_properties功能
    • wrapped 被包装函数
    • 元组WRAPPER_ASSIGNMENTS中是要被覆盖的属性,'__module__', '__name__', '__qualname__', '__doc__', '__annotations__'  模块名、名称、限定名、文档、参数注解
    • 元组WRAPPER_UPDATES中是要被更新的属性,__dict__属性字典
    • 增加一个__warpped__属性,保留着warpped函数
    import datetime, time, functools
    def logger(duration, func=lambda name, duration: print('{} took {}s'.format(name, duration))):
        def _logger(fn):
            @functools.wraps(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
    @logger(5) # add = logger(5)(add)
    def add(x,y):
        time.sleep(1)
        return x + y
    
    print(add(5, 6), add.__name__, add.__wrapped__, add.__dict__, sep='
    ')
  • 相关阅读:
    hdu 1998 奇数阶魔方(找规律+模拟)
    巧用百度Site App新组件为企业官网打造移动站
    POJ 1276 Cash Machine
    Unity3D中使用MiniJson解析json的例子
    设计模式读书笔记-----单例模式
    android 常用资料
    Objective-C之run loop详解
    icon 图标下载
    揭开Html 标签的面纱,忘不了的html .
    12157
  • 原文地址:https://www.cnblogs.com/xzkzzz/p/11377065.html
Copyright © 2011-2022 走看看