ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

update_wrapper和wraps

2022-08-01 16:03:36  阅读:115  来源: 互联网

标签:__ wraps update wrapper print inner test


前言

被装饰器 装饰过后的对象,其实已经不是原来的那个对象了,测试如下:

def dec(func):
    def inner(*args,**kwargs)->None:
        '''inner __doc__'''
        print('do something')
        func()
    return inner

@dec
def test(a:int):
    '''test __doc__'''
    pass

print(test.__doc__)		#inner __doc__
print(test.__name__)	#inner
print(test.__qualname__)#dec.<locals>.inner	
print(test.__annotations__)#{'return': None}

test函数已经成了 inner函数了,如果需要维持原来的test函数的这些 值,做法如下:

from functools import update_wrapper,wraps

def dec(func):
    def inner(*args,**kwargs)->None:
        '''inner __doc__'''
        print('do something')
        func()
    # 加一行代码
    update_wrapper(inner,func)
    return inner

@dec
def test(a:int):
    '''test __doc__'''
    pass

print(test.__doc__)
print(test.__name__)
print(test.__qualname__)
print(test.__annotations__)

输出如下:

test __doc__
test
test
{'a': <class 'int'>}

分析

如上测试结果,update_wrapper接收了两个参数,第一个为被修改属性的函数,第二个参数为提供修改值的函数,这样就把 第一个函数的部分属性改成了第二个函数的属性

查看update_wrapper源码

WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__',
                       '__annotations__')
WRAPPER_UPDATES = ('__dict__',)
def update_wrapper(wrapper,
                   wrapped,
                   assigned = WRAPPER_ASSIGNMENTS,
                   updated = 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, {}))
    wrapper.__wrapped__ = wrapped
    return wrapper

流程如下:

  • update_wrapper接收四个参数,第一个为要修改的对象,第二个为提供修改值的对象,assigned默认接收的值为:WRAPPER_ASSIGNMENTSupdated 默认接收的值为 WRAPPER_UPDATES
  • 遍历WRAPPER_ASSIGNMENTS,将wrapped的所有在该元祖的属性全部赋给wrapper,不在该元祖内的属性不会修改
  • 遍历WRAPPER_UPDATES,将wrapped的所有在该元祖的属性全部赋给wrapper,不在该元祖内的属性不会修改【默认是__dict__

所以回到举例中的 update_wrapper(inner,func) 将test的__moudle__ __name__ qualname__ __doc__ __annotations__ 的值覆盖了inner, 所以最后输出的是 test的这些值

wraps

获得和上述 update_wrapper相同的效果,使用wraps即可,输出的也还是test函数的原来的那些值

from functools import update_wrapper,wraps

def dec(func):
    #加个装饰器
    @warps(func)
    def inner(*args,**kwargs)->None:
        '''inner __doc__'''
        print('do something')
        func()
    return inner

@dec
def test(a:int):
    '''test __doc__'''
    pass

print(test.__doc__)
print(test.__name__)
print(test.__qualname__)
print(test.__annotations__)

查看wraps源码

def wraps(wrapped,
          assigned = WRAPPER_ASSIGNMENTS,
          updated = WRAPPER_UPDATES):
    return partial(update_wrapper, wrapped=wrapped,
                   assigned=assigned, updated=updated)

接收三个参数,其中两个有默认值,另外一个为wrapped【提供修改属性的对象】, 返回一个偏函数对象,偏函数对象的 func为 update_wrapper,固定了参数wrapped,以及 assigned 和 updated。

这时候相当于wraps(test) 就是一个update_wrapper函数,然后作为装饰器装饰inner,将inner作为update_wrapper中wrapper[要被修改属性的对象]的实参,最后返回的inner其实是已经被update_wrapper处理过的函数

测试一下:

print(test.__wrapped__) # {'__wrapped__': <function test at 0x000001E09D1BA940>}

其实这里的test是inner,只是inner被修改了那些属性,然后这里的__wrapped__ 的值对应 update_wrapper源码中的wrapper.__wrapped__ = wrapped

总结

  • 写装饰器的时候,要保持被装饰的函数维持原来的属性,通用写法是在装饰器函数的内部函数前加上@wraps(外部函数的参数),通用模板如下:
from functools import wraps

def outer(par):
    @wraps(par)
    def inner(*args.**kwargs):
        print('do something')
        par()
    return inner
  • update_wrapper django cbv中有典型应用

    as_view的源码片段如下:

    # take name and docstring from class
    update_wrapper(view, cls, updated=())
    
    # and possible attributes set by decorators
    # like csrf_exempt from dispatch
    update_wrapper(view, cls.dispatch, assigned=())
    

标签:__,wraps,update,wrapper,print,inner,test
来源: https://www.cnblogs.com/alantammm/p/16540613.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有