测试环境:Python3.6.2 + win10 +  Pycharm2017.3


装饰器之functools模块的wraps的用途:

首先我们先写一个装饰器

# 探索functools模块wraps装饰器的用途
from functools import wraps def trace(func):
""" 装饰器 """ # @wraps(func)
def callf(*args, **kwargs):
""" A wrapper function """
print("Calling function:{}".format(func.__name__)) # Calling function:foo
res = func(*args, **kwargs)
print("Return value:{}".format(res)) # Return value:9
return res return callf @trace
def foo(x):
""" 返回给定数字的平方 """
return x * x if __name__ == '__main__':
print(foo(3)) #
print(foo.__doc__)
help(foo)
print(foo.__name__)
# print(foo.__globals__)
t = trace(foo)
print(t)
打印结果:
Calling function:foo
Return value:9
9
A wrapper function
Help on function callf in module __main__: callf(*args, **kwargs)
A wrapper function callf
<function trace.<locals>.callf at 0x0000022F744D8730>

不带wraps的装饰器示例

上面的装饰器例子等价于:trace(foo(3)),只是在使用装饰器时,我们不用再手动调用装饰器函数;

如果把这段代码提供给其他人调用, 他可能会想看下foo函数的帮助信息时:

>>>from xxx import foo
>>>help(foo) # print(foo__doc__)
Help on function callf in module __main__: callf(*args, **kwargs)
A wrapper function

这里,他可能会感到迷惑,继续敲:

>>> print(foo.__name__)
callf

最后, 他可能会看源码,找问题原因,我们知道Python中的对象都是"第一类"的,所以,trace函数会返回一个callf闭包函数,连带callf的上下文环境一并返回,所以,可以解释我们执行help(foo)的到结果了

那么,我们如果才能得到我们想要的foo的帮助信息呢,这里就要用到了functools的wraps了。

# 探索functools模块wraps装饰器的用途
from functools import wraps def trace(func):
""" 装饰器 """ @wraps(func)
def callf(*args, **kwargs):
""" A wrapper function """
print("Calling function:{}".format(func.__name__)) # Calling function:foo
res = func(*args, **kwargs)
print("Return value:{}".format(res)) # Return value:9
return res return callf @trace
def foo(x):
""" 返回给定数字的平方 """
return x * x if __name__ == '__main__':
print(foo(3)) #
print(foo.__doc__)
help(foo)
print(foo.__name__)
# print(foo.__globals__)
t = trace(foo)
print(t)

增加wraps的装饰器

至于wraps的原理,通过下面部分源码,可以自行研究,在此就不予展开扩展了

# 有关wraps的源码,有兴趣的可以自行研究下

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 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) class partial:
"""New function with partial application of the given arguments
and keywords.
""" __slots__ = "func", "args", "keywords", "__dict__", "__weakref__" def __new__(*args, **keywords):
if not args:
raise TypeError("descriptor '__new__' of partial needs an argument")
if len(args) < 2:
raise TypeError("type 'partial' takes at least one argument")
cls, func, *args = args
if not callable(func):
raise TypeError("the first argument must be callable")
args = tuple(args) if hasattr(func, "func"):
args = func.args + args
tmpkw = func.keywords.copy()
tmpkw.update(keywords)
keywords = tmpkw
del tmpkw
func = func.func self = super(partial, cls).__new__(cls) self.func = func
self.args = args
self.keywords = keywords
return self def __call__(*args, **keywords):
if not args:
raise TypeError("descriptor '__call__' of partial needs an argument")
self, *args = args
newkeywords = self.keywords.copy()
newkeywords.update(keywords)
return self.func(*self.args, *args, **newkeywords) @recursive_repr()
def __repr__(self):
qualname = type(self).__qualname__
args = [repr(self.func)]
args.extend(repr(x) for x in self.args)
args.extend(f"{k}={v!r}" for (k, v) in self.keywords.items())
if type(self).__module__ == "functools":
return f"functools.{qualname}({', '.join(args)})"
return f"{qualname}({', '.join(args)})" def __reduce__(self):
return type(self), (self.func,), (self.func, self.args,
self.keywords or None, self.__dict__ or None) def __setstate__(self, state):
if not isinstance(state, tuple):
raise TypeError("argument to __setstate__ must be a tuple")
if len(state) != 4:
raise TypeError(f"expected 4 items in state, got {len(state)}")
func, args, kwds, namespace = state
if (not callable(func) or not isinstance(args, tuple) or
(kwds is not None and not isinstance(kwds, dict)) or
(namespace is not None and not isinstance(namespace, dict))):
raise TypeError("invalid partial state") args = tuple(args) # just in case it's a subclass
if kwds is None:
kwds = {}
elif type(kwds) is not dict: # XXX does it need to be *exactly* dict?
kwds = dict(kwds)
if namespace is None:
namespace = {} self.__dict__ = namespace
self.func = func
self.args = args
self.keywords = kwds

wraps源码示例

只要是知道,wraps是通过partial和update_wrapper来帮我们实现想要的结果的


摘自:

  https://www.cnblogs.com/myd7349/p/how_to_use_wraps_of_functools.html    # 关于functools模块的wraps装饰器


End

最新文章

  1. 后台访问 JS解决跨域问题
  2. react.js
  3. gulp问题
  4. (转载)U-boot启动完全分析
  5. eclipse安装JAVA反编译插件
  6. javascript学习-原生javascript的小特效(原生javascript实现链式运动)
  7. java的nio之:java的nio系列教程之selector
  8. Oracle 11g 数据库自动备份执行脚本
  9. 无法解决 equal to 运算中 &amp;quot;Chinese_PRC_CI_AS&amp;quot; 和 &amp;quot;SQL_Latin1_General_CP1_CI_AS&amp;quot; 之间的排序规则冲突。
  10. gtest功能测试一
  11. javascript 闭包暴露句柄和命名冲突的解决方案
  12. IOS拷贝文件到沙盒
  13. Scala中的apply实战详解
  14. hdu 1316 How Many Fibs? (模拟高精度)
  15. Luogu 3371【模板】单源最短路径
  16. Scala简介、安装、函数、面向对象
  17. LaTex Verbatim 环境下使用数学符号
  18. ionic框架使用步骤
  19. 因缺失log4j.properties 配置文件导致flume无法正常启动。
  20. django+uwsgi+nginx数据表过大引起&quot;out of memory for query result&quot;

热门文章

  1. Linux学习笔记:常用100条命令(二)
  2. docker每次都重新拉取远程镜像的问题
  3. ROSETTA使用技巧随笔--控制Log输出等级
  4. 开源unittest测试报告源码BSTestRunner.py
  5. nodejs发送邮件
  6. 移动端的rem适配
  7. JavaScript-模拟收银台小程序
  8. Unity之fragment shader中如何获得视口空间中的坐标
  9. kali linux wifi破解(aircrack)
  10. Python 7 -- 文件存储数据