关于functools模块的wraps装饰器用途
测试环境: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
关于functools模块的wraps装饰器用途的更多相关文章
- 探究functools模块wraps装饰器的用途
<A Byte of Python>17.8节讲decorator的时候,用到了functools模块中的一个装饰器:wraps.因为之前没有接触过这个装饰器,所以特地研究了一下. 何谓“ ...
- wraps装饰器的作用
装饰器的本质是一个闭包函数,作用在于不改变原函数功能和调用方法的基础上给它添加额外的功能.装饰器在装饰一个函数时,原函数就成了一个新的函数,也就是说其属性会发生变化,所以为了不改变原函数的属性,我们会 ...
- python中functools.wraps装饰器的作用
functools.wraps装饰器用于显示被包裹的函数的名称 import functools def node(func): #@functools.wraps(func) def wrapped ...
- wraps装饰器的使用
functools模块中的wraps装饰器 说明 使用functools模块提供的wraps装饰器可以避免被装饰的函数的特殊属性被更改,如函数名称__name__被更改.如果不使用该装饰器,则会导致函 ...
- python学习笔记2-functools.wraps 装饰器
wraps其实没有实际的大用处, 就是用来解决装饰器导致的原函数名指向的函数 的属性发生变化的问题: 装饰器装饰过函数func, 此时func不是指向真正的func,而是指向装饰器中的装饰过的函数 i ...
- python functools.wraps装饰器模块
# -*-coding=utf-8 -*-#实现一个函数执行后计算执行时间的功能 __author__ = 'piay' import time, functools def foo(): ''' 定 ...
- python wraps装饰器
这是一个很有用的装饰器.看过前一篇反射的朋友应该知道,函数是有几个特殊属性比如函数名,在被装饰后,上例中的函数名foo会变成包装函数的名字 wrapper,如果你希望使用反射,可能会导致意外的结果.这 ...
- django的权限认证:登录和退出。auth模块和@login_required装饰器
在settings.py中配置LOGIN_URL参数: # 用户访问带有(@login_required)标签的页面(view)时,如果没有登录,就会跳转到LOGIN_URL(即登陆url). LOG ...
- Django内置auth模块中login_required装饰器用于类视图的优雅方式
使用多继承 以及类似java中的静态代理模式 原理:OrderView.as_view()根据广度优先,调用的是LoginRequiredMixin中的as_view(cls, *args, **kw ...
随机推荐
- Ext.define细节分析
自己写的其实还是不懂,再看看别人写的吧Extjs4 源码分析系列一 类的创建过程https://www.cnblogs.com/creazyguagua/p/4302864.htmlhttp://ww ...
- winrar自解压释放路径详解
因为在 WINRAR 的帮助文件中没有对自解压路径和系统的环境变量之间作说明,所以,很多人只知道,其自解压路径可以智能定位到系统的 PROGRAM FILES 目录,而不知道它其实还可以智能定位到系统 ...
- .yml文件格式
http://yaml.org/ YAML: YAML Ain't Markup Language What It Is: YAML is a human friendly data serializ ...
- iOSOpenDev安装使用
下载:http://iosopendev.com/download/ 选择“iOSOpenDev 1.6-2 Installer” 出错解决方法 https://www.jianshu.com/p/2 ...
- cocos2d-x JS 加载播放Studio帧动画的两种方法
昨天懵逼的搞了两个多小时(百度无果/没看出什么矛头),自己琢磨总算搞出来了 1. var levelUpJson = ccs.load("res/LevelUp.json") ...
- PDO数据访问抽象层(上)
PDO比MySQLi功能强大 PDO可以访问MySQL及其它数据库 一.造对象 <?php $dsn = "mysql:dbname = crud;host = localhost&q ...
- Python记录4:文件操作
###文件 ''' 1. 什麽是文件 文件是操作系統為用戶/应用程序提供一種操作硬盤的虚拟单位 2. 爲何要用文件 为了存取硬盘数据 3. 如何用文件 #1. 打開文件 #2. 读写文 ...
- 软工网络15团队作业4——Alpha阶段敏捷冲刺5.0
1.每天举行站立式会议,提供当天站立式会议照片一张. 2.项目每个成员的昨天进展.存在问题.今天安排. 成员 昨天已完成 今天计划完成 郭炜埕 完善新建话题界面 实现前端各界面的跳转连接 郑晓丽 进行 ...
- QT5中QByteArray转QString中文乱码
1.添加头文件 #include <QTextCodec> 2.用QTextCodec 设置格式转换 QByteArray barr; barr.insert(0,(char*)(pMsg ...
- v1版本
<?php use yii\helpers\Html; use yii\helpers\Url; use yii\widgets\DetailView; use yii\grid\GridVie ...