zoukankan      html  css  js  c++  java
  • 再看装饰器

    其他资料
    https://www.cnblogs.com/wkhzwmr/p/15282810.html
    https://www.cnblogs.com/wkhzwmr/p/15085126.html

    基本代码实现

    用函数的形式来表示

    def add(x,y):
        return x+y
    
    def test(): # 调用add函数传入1,2参数
        print(add(1,2))
    
    test() # 3
    

    装饰器的形式

    @log
    def add(x,y):
        return x + y
    

    实现过程类似以下的函数

    没有更换函数的名字,用函数名来调用函数,就可以达到类似的扩展功能

    def add(x,y):
        return x + y
    add = log(add)
    

    tips:AOP编程,面向切面编程。不修改目标源码的前提下,添加功能的计数手段或设计模式,是对OOP的补充;OOP面向对象编程

    具体实现

    比单纯的从一个函数中调用另外一个函数要复杂的多;
    基本代码而且还没有函数参数传递;
    函数装饰器是装饰器作用在函数上,原装饰器可用函数编写也可用类编写

    通过包装函数间接调用原函数

    def log(fn):
        def wrap(*args,**kwargs): # 原函数是装饰器下方的fn
            print(f'log:{args},{kwargs}')
            return fn(*args,**kwargs)
        
        return wrap # 返回包装函数替代原函数与名字关联
    
    @log
    def add(x,y):
        return x + y
    add(1,2) # log:(1, 2),{}
    # 打印出有打印的语句或return具体的返回值
    print(add(1,2)) # log:(1, 2),{}   3
    

    用匿名函数来写

    def log(fn):
        #### fn.log_func为lambda生成的函数名,需调用
        fn.log_func = lambda *args, **kwargs:print(f'log:{args},{kwargs}')
        fn.log_func()
        return fn
    @log
    def add(x,y):
        return x + y
    print(add(1,2)) # log:(),{} 3
    

    类装饰器

    任何可调用对象(callable)都可用来实现装饰器模式

    class log:
        def __init__(self,fn):
            self.fn = fn
        
        def __call__(self,*args, **kwargs):
            print(f'log:{args},{kwargs}')
            return self.fn(*args, **kwargs)
    
    @log
    def add(x,y):
        return x + y
    add(1,2) # log:(1, 2),{}
    print(add(1,2))  #log:(1, 2),{} 3
    

    把类装饰器添加到类中;不推荐

    类实现的装饰器应用实例方法时,会导致方法绑定丢失

    class X:
        @log
        def test(self): # #  方法被装饰器实例所替代 
            pass
    x = X()
    
    x.test # log:(),{}   
    print(x.test) # <__main__.log object at 0x0000012D1FA50C50>  log:(),{}
    try:
        x.test()
    except Exception as e:
        print(e) # test() missing 1 required positional argument: 'self'
    

    把函数装置器添加到类中

    有无参数装饰器的本质区别在于,被修饰的原函数参数在外壳函数里(这里的log)还是内层函数中(这里的wrap)

    def log(fn): # 
        def wrap(*args,**kwargs): # 通过包装函数间接调用原函数;原函数是装饰器下方的函数
            print(f'log:{args},{kwargs}')
            return fn(*args,**kwargs)
        
        return wrap # 返回包装函数替代原函数与名字关联
    class X:
        @log
        def test(self):
            pass
    x = X()
    print(x.test) # <bound method log.<locals>.wrap of <__main__.X object at 0x0000020A715C0D30>
    # 这里与类装饰器的结果不同的原因是
    ## 函数对象默认实现了描述符协议和绑定规则
    print(log.__get__) # <method-wrapper '__get__' of function object at 0x0000019EC98497B8>
    

    装饰器的嵌套

    一个函数定义可以被一个或多个 decorator 表达式所包装。
    当函数被定义时将在包含该函数定义的作用域中对装饰器表达式求值。
    求值结果必须是一个可调用对象,它会以该函数对象作为唯一参数被发起调用。
    其返回值将被绑定到函数名称而非函数对象

    @f1(arg)
    @f2
    def func(): pass
    

    等价于

    func = f1(arg)(f2(func))
    

    f1无参数的话就是

    func  = f1(f2(func))
    

    装饰器的参数

    装饰器带有参数

    def log(name='default'): # 外层函数接收参数
        print(f'args:{name}')
    
        def decorator(fn): # 装饰器
            print(f'decorator:{fn}')
            return fn # 返回包装函数或原函数
        return decorator
    
    @log('demo')
    def test():
        pass
    # 装饰器已经运行了,执行结果如下
    '''
    args:demo
    decorator:<function test at 0x00000183D84198C8>
    '''
    

    对于有参数的装饰器,即便是默认值也需加括号

    @log()
    def test():
        pass
    ## 执行结果如下
    '''
    args:default
    decorator:<function test at 0x000002AFA47A9950>
    '''
    

    装饰器的属性

    包装函数(装饰器)需更像原函数,如拥有某些相同的属性
    装饰器functools.wraps将原函数__module__、namedoc、__annotations__等属性复制到包装函数
    还用__wrapped__存储原始函数或上衣装饰器返回值。可据此判断并绕开装饰器对单元测试的干扰

    import functools
    def log(fn):
        @functools.wraps(fn)
        def wrap(*args,**kwargs): # 通过包装函数间接调用原函数;原函数是装饰器下方的函数
            # print(f'log:{args},{kwargs}')
            return fn(*args,**kwargs)
        print(f'wrap:{id(wrap)},finc:{id(fn)}')
        return wrap # 返回包装函数替代原函数与名字关联
    @log
    def add(x,y):
        return x + y
    
    add.__name__ # wrap:1264671299784,finc:1264671299648
    print(add.__annotations__) # {}
    print(id(add),id(add.__wrapped__)) # 1264671299784 1264671299648
    

    类型装饰器

    类型装饰器是修饰类的
    类型装饰器是直接作用在类型上的,与函数装饰器的区别是接收参数为类型对象(类)

    装饰器-函数中返回类

    def log(cc): 
        class wrapper:
            def __init__(self,*args, **kwargs):
                self.__dict__['inst'] = cc(*args,**kwargs)
            
            # 下方就是描述符吧,一点也看不懂
            def __getattr__(self,name):
                value = getattr(self.inst,name) # .inst啥意思
                print(f'get:{name}={value}')
                return value
            def __setattr__(self,name,value):
                print(f'set: {name} = {value}')
                return setattr(self.inst,name,value)
        return wrapper
    
    @log
    class X:
        pass
    x = X()
    x.a = 1 # set: a = 1
    x.a # get:a=1
    

    装饰器——函数中返回函数

    间接调用目标构造方法创建实例

    def log(cls):
        def wrap(*args, **kwargs):
            o = cls(*args,**kwargs) # 间接调用目标构造方法创建实例
            print(f'log : {o}')
            return o
        return wrap
    
    @log
    class X:
        pass
    
    X() # log : <__main__.X object at 0x000001CB72B30A90>
    

    装饰器的应用

    调用跟踪

    记录目标调用参数、返回值。以及执行次数和执行时间等信息

    def call_count(fn):
        def counter(*args, **kwargs):
            counter.__count__ +=1
            return fn(*args, **kwargs)
        
        counter.__count__ = 0
        return counter
    @call_count
    def a():
        pass
    
    @call_count
    def b():
        pass
    # 还可以用分号
    # 记录了a调用了2次
    a();a();print(a.__count__) # 2
    # 记录了b调用了3次
    b();b();b();print(b.__count__) # 3
    

    标准库类似的应用,通过缓存结果减少目标执行次数

    import functools
    @functools.lru_cache(10)
    

    属性管理

    为目标添加额外属性,在原有设计上以装配方式混入(mixin)其他功能组

    def pet(cls):
        cls.dosomething = lambda self:None
        return cls
    @pet  # 添加宠物功能
    class Parrot:
        pass
    

    实例管理

    替代目标构造方法,拦截实例的创建。用于实现对象缓存,或单例模式

    def singleton(cls):
        inst = None
        def wrap(*args, **kwargs):
            nonlocal inst
            if not inst: inst = cls(*args, **kwargs)
            return inst
        return wrap
    
    @singleton
    class X:
        pass # 
    print(X() is X()) # True
    

    部件注册

    在很多Web框架里,用装饰器替代配置文件实现路由注册

    class App:
        def __init__(self):
            self.routers = {}
        def route(self,url): # 实现为带参数的装饰器,用于注册路由配置
            def register(fn):
                self.routers[url] = fn
                return fn
            return register
    
    app = App()
    @app.route('/')
    def index():
        pass
    @app.route('/help')
    def help():
        pass
    
    # 打印类中的routers属性
    print(app.routers) # {'/': <function index at 0x000001B7F9129E18>, '/help': <function help at 0x000001B7F9129EA0>}
    

    笔记来源《Python3学习笔记(上卷)》

  • 相关阅读:
    [C# 基础知识系列]专题六:泛型基础篇——为什么引入泛型
    [C#基础知识系列]专题十七:深入理解动态类型
    [C# 基础知识系列]专题九: 深入理解泛型可变性
    C#网络编程系列文章索引
    [C#基础知识系列]专题十:全面解析可空类型
    [C# 基础知识系列]专题十一:匿名方法解析
    [C# 基础知识系列]专题十六:Linq介绍
    VSTO之旅系列(一):VSTO入门
    [C# 网络编程系列]专题七:UDP编程补充——UDP广播程序的实现
    [C# 网络编程系列]专题四:自定义Web浏览器
  • 原文地址:https://www.cnblogs.com/wkhzwmr/p/15580848.html
Copyright © 2011-2022 走看看