• python——装饰器深入研究(二)


    在这里插入图片描述

    一、装饰器的副作用

    函数名称.__name__:获取函数名称
    函数名称.__doc__:获取函数注释

    def work1():
        """
        函数的文档字符串注释
        :return:
        """
        print("函数--work1---")
    
    print('函数名称:',work1.__name__)
    print('函数文档注释:',work1.__doc__)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    执行结果:当函数没有被装饰器装饰时的执行结果为:

    函数名称: work1
    函数文档注释: 
        函数的文档字符串注释
        :return:
        
    
    • 1
    • 2
    • 3
    • 4
    • 5

    二、函数被装饰器装饰后

    def decorator(func):
    
        def wrapper(*args,**kwargs):
    
            res=func(*args,**kwargs)
    
        return wrapper
    
    
    
    @decorator
    def work1():
        """
        函数的文档字符串注释
        :return:
        """
        print("函数--work1---")
    
    print('函数名称:',work1.__name__)
    print('函数文档注释:',work1.__doc__)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    执行结果:函数被装饰器装饰后的执行结果

    函数名称: wrapper
    函数文档注释: None
    
    • 1
    • 2

    三、副作用:

    由于装饰器装饰了之后,原函数名字指向的是装饰器内部的闭包,
    因此会产生副作用,无法在通过函数名,去正常获取原函数的属性

    四、副作用消除

    4.1、wraps的作用:获取装饰器中所传的函数的属性,并且把属性给被装饰器装饰的函数中

    def user():
        """用户函数"""
    
    @wraps(user)
    def login():
        '''定义函数'''
        print('登录')
    print("login函数的名字:",login.__name__)
    print("login函数的文档注释:",login.__doc__)
    
    login()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    执行结果:

    login函数的名字: user
    login函数的文档注释: 用户函数
    登录

    4.2、从4.1函数为例进行源码分析:

    wraps源码:

    def wraps(wrapped,
              assigned = WRAPPER_ASSIGNMENTS,
              updated = WRAPPER_UPDATES):
        """Decorator factory to apply update_wrapper() to a wrapper function
    
           Returns a decorator that invokes update_wrapper() with the decorated
           function as the wrapper argument and the arguments to wraps() as the
           remaining arguments. Default arguments are as for update_wrapper().
           This is a convenience function to simplify applying partial() to
           update_wrapper().
        """
        return partial(update_wrapper, wrapped=wrapped,
                       assigned=assigned, updated=updated)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    update_wrapper()源码

    WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__',
                           '__annotations__')
    WRAPPER_UPDATES = ('__dict__',)
    def update_wrapper(wrapper,
                       wrapped,
                       assigned = WRAPPER_ASSIGNMENTS,
                       updated = WRAPPER_UPDATES):
        """Update a wrapper function to look like the wrapped function
    
           wrapper is the function to be updated
           wrapped is the original function
           assigned is a tuple naming the attributes assigned directly
           from the wrapped function to the wrapper function (defaults to
           functools.WRAPPER_ASSIGNMENTS)
           updated is a tuple naming the attributes of the wrapper that
           are updated with the corresponding attribute from the wrapped
           function (defaults to functools.WRAPPER_UPDATES)
        """
        for attr in assigned:
            try:
                value = getattr(wrapped, attr)
            except AttributeError:
                pass
            else:
                setattr(wrapper, attr, value)
        for attr in updated:
            getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
        # Issue #17482: set __wrapped__ last so we don't inadvertently copy it
        # from the wrapped function when updating __dict__
        wrapper.__wrapped__ = wrapped
        # Return the wrapper so this can be used as a decorator via partial()
        return wrapper
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    解析图

    在这里插入图片描述
    相当于把user属性一个一个的拿出来添加到login函数中

    4.3、消除副作用

    @wraps(func) :将func(work1)函数的属性复制一份给wrapper

    from functools import wraps
    def decorator(func):
    
        @wraps(func)                    #将func(work1)函数的属性复制一份给wrapper
        def wrapper(*args,**kwargs):
    
            res=func(*args,**kwargs)
    
        return wrapper
    
    
    
    @decorator
    def work1():
        """
        函数的文档字符串注释
        :return:
        """
        print("函数--work1---")
    
    print('函数名称:',work1.__name__)
    print('函数文档注释:',work1.__doc__)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    执行结果

    函数名称: work1
    函数文档注释:
    函数的文档字符串注释
    :return:

    五、通过装饰器对函数和类进行属性添加和修改

    5.1、给函数添加属性

    函数名称.属性名称=‘属性值’
    函数名称.__dict__:获取函数所有的属性和方法

    def work1():
        """
        函数的文档字符串注释
        :return:
        """
        print("函数--work1---")
    
    #函数对象的属性
    print('函数名称:',work1.__dict__)
    #print(type(work1))
    
    work1.desc='给函数添加属性'
    work1.age=18
    print('函数名称:',work1.__dict__)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    函数名称: {}
    函数名称: {‘desc’: ‘给函数添加属性’, ‘age’: 18}

    5.2、通过装饰器给函数添加属性

    特别注意:

    闭包形式的装饰器一般用于:拓展功能
    普通函数作为装饰器:一般用于对函数和类的属性进行修改添加,案例如下

    def add_attr(func):
        func.age=1
        func.data=[2,8,23,22,24]
    
        return func
    
    @add_attr           #todo work1=add_attr(work1)
    def work1():
        """
        函数的文档字符串注释
        :return:
        """
        print("函数--work1---")
    
    #函数对象的属性
    print('函数名称:',work1.__dict__)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    执行结果:
    函数名称: {‘age’: 1, ‘data’: [2, 8, 23, 22, 24]}

    执行逻辑:

    1、@add_attr :将函数work1作为参数传递给装饰器add_attr;
    2、并且将add_attr(work1)用与函数同名的变量work1来接收,即work1=add_attr(work1)
    3、调用work1()即执行func
    4、装饰器内部实现的功能给函数work1添加属性age、data

    5.3、通过装饰器给类添加属性

    def add_attr(func):
        func.age=1
        func.data=[2,8,23,22,24]
    
        return func
    
    @add_attr
    class Demo:
        pass
    
    print(Demo.__dict__)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    执行结果:新增了属性:‘age’: 1, ‘data’: [2, 8, 23, 22, 24]
    {‘module’: ‘main’, ‘dict’: dict’ of ‘Demo’ objects>, ‘weakref’: weakref’ of ‘Demo’ objects>, ‘doc’: None, ‘age’: 1, ‘data’: [2, 8, 23, 22, 24]}

    在这里插入图片描述

  • 相关阅读:
    C++手撕红黑树
    k8s部署mysql一主两从
    基于STC12C5A60S2系列1T 8051单片机实现串口调试助手软件与单片机相互发送数据的RS485通信功能
    LOG4J
    【前端】Nesj 学习笔记
    Unity与Js通信交互
    阿里P9整理出千页笔记:Java程序设计与数据结构基础+进阶
    文件的物理结构
    关于el-input和el-select宽度不一致问题解决
    flinksql BUG : flink hologres-cdc source FINISHED
  • 原文地址:https://blog.csdn.net/YZL40514131/article/details/126677507