标签:__ 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_ASSIGNMENTS
,updated
默认接收的值为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. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。