@wraps作用

python中的装饰器装饰过的函数其实就不是函数本身了,我们可以看看下面的例子

import time
def timmer(func):
"""timmer doc"""
def inner(*args, **kwargs):
"""inner doc"""
start = time.time()
res = func(*args, **kwargs)
end = time.time()
print("函数运行时间为 %s" % (end - start))
return res
return inner @timmer
def func_test():
"""func_test doc"""
time.sleep(2)
return print(func_test.__name__) # inner
print(func_test.__doc__) # inner doc

按我们正常的思维,func_test.__name__应该拿到的就是“func_test”,所以这个结果就印证了上面的第一句话,但是这是我们加一个@wraps,就会发现好像一切都正常了:

import time
from functools import wraps
def timmer(func):
"""timmer doc"""
@wraps(func)
def inner(*args, **kwargs):
"""inner doc"""
start = time.time()
res = func(*args, **kwargs)
end = time.time()
print("函数运行时间为 %s" % (end - start))
return res
return inner @timmer
def func_test():
"""func_test doc"""
time.sleep(2)
return print(func_test.__name__) # func_test
print(func_test.__doc__) # func_test doc

@wraps的实现原理

为了方便理解,我把源码和例子放在了一起,这样的话我们看着会方便:

import time
from functools import partial WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__',
'__annotations__')
WRAPPER_UPDATES = ('__dict__',) def update_wrapper(wrapper, # inner
wrapped, # func_test
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)
"""
print('update_wrapper 执行...')
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()
print('update_wrapper 执行结束')
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().
"""
print('wraps 执行...')
print('wraps 执行结束') # 纯粹为了打印出来的结果好理解
return partial(update_wrapper, wrapped=wrapped,
assigned=assigned, updated=updated) def timmer(func):
print('timmer 执行...') @wraps(func) # inner = update_wrapper的返回值
def inner(*args, **kwargs):
start = time.time()
res = func(*args, **kwargs)
end = time.time()
print("函数运行时间为 %s" % (end - start))
return res print('timmer 执行结束') # 当然不是真正的结束,执行完下一行才结束
return inner @timmer
def func_test():
print("func_test 执行...")
time.sleep(2)
print("func_test 运行结束")
return func_test() """
打印结果如下:
timmer 执行...
wraps 执行...
wraps 执行结束
update_wrapper 执行...
update_wrapper 执行结束
timmer 执行结束
func_test 执行...
func_test 运行结束
函数运行时间为 2.0000197887420654 从打印的结果我们可以看出,@语法会在函数定义或者说模块初始化阶段(可能称呼不对,以后回来改)就执行了
"""

上面的例子中我加了很多打印,主要是为了提醒一下在func_test()函数执行之前,@语法已经执行了。

其实原理很简单,用了一个偏函数,去执行update_wrapper,真正起作用的也是这个函数,func_test执行之前,update_wrapper函数就会把inner函数的好多属性(示例中WRAPPER_ASSIGNMENTS,WRAPPER_UPDATES指向的属性 ,还有__wrapped__属性)全部其换成func_test的属性。

python@wraps实现原理的更多相关文章

  1. Python分布式爬虫原理

    转载 permike 原文 Python分布式爬虫原理 首先,我们先来看看,如果是人正常的行为,是如何获取网页内容的. (1)打开浏览器,输入URL,打开源网页 (2)选取我们想要的内容,包括标题,作 ...

  2. Python Socket通信原理

    [Python之旅]第五篇(一):Python Socket通信原理   python Socket 通信理论 socket例子 摘要:  只要和网络服务涉及的,就离不开Socket以及Socket编 ...

  3. python解释执行原理(转载)

    Python解释执行原理 转自:http://l62s.iteye.com/blog/1481421 这里的解释执行是相对于编译执行而言的.我们都知道,使用C/C++之类的编译性语言编写的程序,是需要 ...

  4. python虚拟机运行原理

    近期为了面试想要了解下python的运行原理方面的东西,奈何关于python没有找到一本类似于深入理解Java虚拟机方面的书籍,找到了一本<python源码剖析>电子书,但是觉得相对来说最 ...

  5. python程序执行原理

    Python程序的执行原理 1. 过程概述 Python先把代码(.py文件)编译成字节码,交给字节码虚拟机,然后解释器一条一条执行字节码指令,从而完成程序的执行. 1.1python先把代码(.py ...

  6. Python进阶----索引原理,mysql常见的索引,索引的使用,索引的优化,不能命中索引的情况,explain执行计划,慢查询和慢日志, 多表联查优化

    Python进阶----索引原理,mysql常见的索引,索引的使用,索引的优化,不能命中索引的情况,explain执行计划,慢查询和慢日志, 多表联查优化 一丶索引原理 什么是索引:       索引 ...

  7. Hive Python Streaming的原理及写法

    在Hive中,须要实现Hive中的函数无法实现的功能时,就能够用Streaming来实现. 其原理能够理解成:用HQL语句之外的语言,如Python.Shell来实现这些功能,同一时候配合HQL语句, ...

  8. 轻松搞懂Python递归函数的原理与应用

    递归: 在函数的定义中,函数内部的语句调用函数本身. 1.递归的原理 学习任何计算机语言过程中,“递归”一直是所有人心中的疼.不知你是否听过这个冷笑话:“一个面包,走着走着饿了,于是就把自己吃了”. ...

  9. 5分钟看懂系列:Python 线程池原理及实现

    概述 传统多线程方案会使用"即时创建, 即时销毁"的策略.尽管与创建进程相比,创建线程的时间已经大大的缩短,但是如果提交给线程的任务是执行时间较短,而且执行次数极其频繁,那么服务器 ...

随机推荐

  1. 洛谷P3366 【模板】最小生成树(Boruvka算法)

    题意 题目链接 Sol 自己yy着写了一下Boruvka算法. 算法思想很简单,就是每次贪心的用两个联通块之间最小的边去合并. 复杂度\(O(n \log n)\),然鹅没有Kruskal跑的快,但是 ...

  2. Android 设计模式之MVC模式

    说到Android设计模式的MVC模式,估计很多人都是比较熟悉了,这里深入了解一下MVC到底是怎么回事,以ListView为例子讲解. 一.深入理解MVC概念 MVC即Model-View-Contr ...

  3. QC API全系列揭秘之Test Execution操作(全网首发)

    (原创文章,转载请注明出处.) 一.QC简介: Quality Center存在至今已经走过了10多个年头,名字从一开始的TD,到后来的QC,再到现在的ALM.所属公司从开始的Mercury到现在的H ...

  4. go的生产者-消费者模式

    package main import ( "fmt" "math/rand" "time" ) // 数据生产者 func produce ...

  5. [idea] SpringBoot整合swagger2实现CRUD

    一:创建SpringBoot ,在pom.xml文件中加入jar包 <dependency> <groupId>io.springfox</groupId> < ...

  6. Pycharm安装并配置jupyter notebook

    Pycharm安装并配置jupyter notebook Pycharm安装并配置jupyter notebook 一: 安装命令jupyter: pip install jupyter 如果缺少依赖 ...

  7. ctrl+shift+r / ctrl+f5 强制(不使用缓存)刷新google chrome网页

    我改了csdn图片后, 一直看到的是旧图片, n天之后, 还是旧图片.猜测应该是用了缓存(且缓存更新逻辑失败, 定是csdn的bug), 用ctrl+shift+r, 或者ctrl+f5, 强制刷新页 ...

  8. pyhton从开始到光棍的day11

    纪念我这个开始学python的光棍天,光棍天依旧是函数,这次的函数有个小高级,比昨天的low函数稍微高级点,就是在使用函数中是可以赋值的,比如索引一个值什么的.函数还可以当做参数进行传递,把这个函数名 ...

  9. Linux 下查看字体

    1.查看所有字体 命令:fc-list 2.查看中文字体 命令:fc-list :lang=zh 3.查看更多字体 命令:fc-match -v "AR PL UKai CN"

  10. Clion快捷键

    快捷键配置 File->Setting->Keymap->Keymaps 选择Visual Studio风格 代码提示的匹配模式 File->Setting->Edito ...