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

一、装饰器的副作用

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

def work1():
    """
    函数的文档字符串注释
    :return:
    """
    print("函数--work1---")

print('函数名称:',work1.__name__)
print('函数文档注释:',work1.__doc__)

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

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

二、函数被装饰器装饰后

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__)

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

函数名称: wrapper
函数文档注释: None

三、副作用:

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

四、副作用消除

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

def user():
    """用户函数"""

@wraps(user)
def login():
    '''定义函数'''
    print('登录')
print("login函数的名字:",login.__name__)
print("login函数的文档注释:",login.__doc__)

login()

执行结果:

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)

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

解析图


相当于把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__)

执行结果

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

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

5.1、给函数添加属性

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

def work1():
    """
    函数的文档字符串注释
    :return:
    """
    print("函数--work1---")

#函数对象的属性
print('函数名称:',work1.__dict__)
#print(type(work1))

work1.desc='给函数添加属性'
work1.age=18
print('函数名称:',work1.__dict__)

函数名称: {}
函数名称: {‘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__)

执行结果:
函数名称: {‘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__)

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

物联沃分享整理
物联沃-IOTWORD物联网 » python——装饰器的深入研究(二)

发表评论