python 自定义装饰器实例详解


Posted in Python onJuly 20, 2019

本文实例讲述了python 自定义装饰器。分享给大家供大家参考,具体如下:

先看一个例子

def deco(func):
  print("before myfunc() called.")
  func()
  print("after myfunc() called.")
  return func
@deco
def myfunc():
  print("myfunc() called.")
# myfunc = deco(myfunc) # 与上面的@deco等价
myfunc()
print("***********")
myfunc()

会发现,输出为

before myfunc() called.
myfunc() called.
after myfunc() called.
myfunc() called.
***********
myfunc() called.

这就是说,装饰器里面的东西只调用了一次,为什么呢?

是因为,在myfunc()函数的定义前面加一句@deco,本质上完全等价于在出现def myfunc()后,先将下面所有内容的首地址传递给func,然后紧接着加上一句 myfunc = deco(myfunc)。执行这句话,表示func代表了本来定义的myfunc()的函数体,同时函数myfunc()的地址传递给deco()函数,即 myfunc -> func,这里就相当于myfunc的值与func的值完全相同了。然后执行装饰器里面的内容,最后返回给func,传递给myfunc。接下来在调用myfunc()的时候,打印输出“myfunc() called”。第二次调用myfunc()函数的时候,依然只打印输出“myfunc() called”。为什么第二次没有执行装饰器里面的内容呢?是因为,myfunc = deco(myfunc)这句话只执行了一次,而这句话,才是真正执行装饰器里面的内容的话。

上面的代码表示,装饰器相当于只对第一次调用他的函数进行了装饰,那么,怎么对每次调用的函数都装饰呢?接着看

def deco(func):
  def wrapper(*args, **kwargs): # *args, **kwargs用于接收func的参数
    print("before myfunc() called.")
    func(*args, **kwargs)
    print("after myfunc() called.")
  return wrapper
@deco
def myfunc(a, b):
  print(a+b)
# myfunc = deco(myfunc) # 与上面的@deco等价
myfunc(1, 2)
print("***********")
myfunc(3, 4)

该代码输出结果为

before myfunc() called.
3
after myfunc() called.
***********
before myfunc() called.
7
after myfunc() called.

我们说了,在myfunc()函数的定义前面加一句@deco,本质上完全等价于在出现def?myfunc()后,先将下面所有内容的首地址传递给func,然后紧接着加上一句 myfunc = deco(myfunc)。执行myfunc(1, 2)命令的时候,myfunc函数体的地址早已经传递给了deco()函数,返回的是wrapper。这是myfunc所代表的地址不再是原来的myfunc的地址,而是wrapper函数的地址。所以,以后凡是出现myfunc()的地方,都是在调用wrapper()函数。即myfunc(1, 2)就是wrapper(1, 2),所以每次调用myfunc()时候,装饰器里面的内容都会被执行了。而wrapper()函数体里面的func,就代表了原来myfunc()的函数体。

怎么进一步理解“在出现def?myfunc()后,先将下面所有内容的首地址传递给func”这句话呢?看:

def deco(func):
  def wrapper(*args, **kwargs): # *args, **kwargs用于接收func的参数
    print("wrapper的地址:", wrapper)
    func(*args, **kwargs)
    print("func的地址:", func)
  return wrapper
@deco
def myfunc(a, b):
  print("myfunc的地址:",myfunc)
  print(a+b)
# myfunc = deco(myfunc) # 与上面的@deco等价
myfunc(1, 2)
print("***********")
print("修改后myfunc的地址:",myfunc)

运行结果:

wrapper的地址: <function deco.<locals>.wrapper at 0x0000023AA9FF58C8>
myfunc的地址: <function deco.<locals>.wrapper at 0x0000023AA9FF58C8>
3
func的地址: <function myfunc at 0x0000023AA9FF5840>
***********
修改后myfunc的地址: <function deco.<locals>.wrapper at 0x0000023AA9FF58C8>

程序执行到myfunc(1,2)的时候,本质上是在执行wrapper(1, 2),于是先输出wrapper的地址,再执行func()函数。执行func()函数的时候,输出myfunc()的地址,(可见,此时myfunc的值与wrapper的是相等),再打印3。当输出func()函数的地址,可见func()函数的地址与myfunc()函数的地址不一样了!!!!这就是说,原来定义的myfunc()函数的函数体,已经属于func了,而不属于myfunc了!!

进一步见证奇迹!!

def deco(func):
  def wrapper(*args, **kwargs): # *args, **kwargs用于接收func的参数
    pass
  return wrapper
@deco
def myfunc(a, b):
  print(a+b)
myfunc(1, 2)

该代码没有任何输出。那是因为,执行myfunc(1, 2)的时候,本质上是执行wrapper(1, 2)。而wrapper(1, 2)又不干任何事情,所以没有输出。至于print(a+b)这句话,他的地址已经属于func了。

带参数的装饰器,可以参见其他文章

更多关于Python相关内容可查看本站专题:《Python数据结构与算法教程》、《Python Socket编程技巧总结》、《Python函数使用技巧总结》、《Python字符串操作技巧汇总》及《Python入门与进阶经典教程》

希望本文所述对大家Python程序设计有所帮助。

Python 相关文章推荐
python使用win32com在百度空间插入html元素示例
Feb 20 Python
Python获取系统所有进程PID及进程名称的方法示例
May 24 Python
3分钟学会一个Python小技巧
Nov 23 Python
Python常用的json标准库
Feb 19 Python
Python中的Socket 与 ScoketServer 通信及遇到问题解决方法
Apr 01 Python
详解Python匿名函数(lambda函数)
Apr 19 Python
Flask框架学习笔记之使用Flask实现表单开发详解
Aug 12 Python
Python集合操作方法详解
Feb 09 Python
虚拟机下载python是否需要联网
Jul 27 Python
基于python模拟TCP3次握手连接及发送数据
Nov 06 Python
Python操作Excel的学习笔记
Feb 18 Python
python内置模块之上下文管理contextlib
Jun 14 Python
Python 列表去重去除空字符的例子
Jul 20 #Python
python列表每个元素同增同减和列表元素去空格的实例
Jul 20 #Python
用Python配平化学方程式的方法
Jul 20 #Python
对python中的float除法和整除法的实例详解
Jul 20 #Python
python从list列表中选出一个数和其对应的坐标方法
Jul 20 #Python
Python实现一个数组除以一个数的例子
Jul 20 #Python
python 环境搭建 及python-3.4.4的下载和安装过程
Jul 20 #Python
You might like
【动漫杂谈】关于《请在T台上微笑》
2020/03/03 日漫
总集篇&特番节目先行播出!《SAO Alicization War of Underworld》第2季度TV动画4月25日放送!
2020/03/06 日漫
PHP新手上路(十)
2006/10/09 PHP
PHP中register_globals参数为OFF和ON的区别(register_globals 使用详解)
2012/02/05 PHP
PHP常用特殊运算符号和函数总结(php新手入门必看)
2013/02/02 PHP
完美解决thinkphp验证码出错无法显示的方法
2014/12/09 PHP
ThinkPHP模板Volist标签嵌套循环输出多维数组的方法
2016/03/23 PHP
PHP中file_put_contents追加和换行的实现方法
2017/04/01 PHP
&amp;lt;script defer&amp;gt; defer 是什么意思
2009/05/10 Javascript
网页中返回顶部代码(多种方法)另附注释说明
2013/04/24 Javascript
在 Node.js 中使用原生 ES 模块方法解析
2017/09/19 Javascript
Vue.js使用axios动态获取response里的data数据操作
2020/09/08 Javascript
[02:52]2014DOTA2西雅图国际邀请赛 CIS战队巡礼
2014/07/07 DOTA
python插入排序算法的实现代码
2013/11/21 Python
Python实现根据IP地址和子网掩码算出网段的方法
2015/07/30 Python
Google开源的Python格式化工具YAPF的安装和使用教程
2016/05/31 Python
Python实现多线程HTTP下载器示例
2017/02/11 Python
Python之日期与时间处理模块(date和datetime)
2017/02/16 Python
matplotlib简介,安装和简单实例代码
2017/12/26 Python
Tensorflow之Saver的用法详解
2018/04/23 Python
Django管理员账号和密码忘记的完美解决方法
2018/12/06 Python
pytorch对可变长度序列的处理方法详解
2018/12/08 Python
python使用selenium实现批量文件下载
2019/03/11 Python
Python实现FM算法解析
2019/06/18 Python
Python数据结构与算法(几种排序)小结
2019/06/22 Python
HTML5不支持标签和新增标签详解
2016/06/27 HTML / CSS
米兰网婚纱礼服法国网上商店:Milanoo法国
2016/08/20 全球购物
美国羊皮公司:Overland
2018/01/15 全球购物
Paul Smith英国官网:英国国宝级时装品牌
2019/03/21 全球购物
德国受欢迎的旅游和休闲网站:lastminute.de
2019/09/23 全球购物
联想阿根廷官方网站:Lenovo Argentina
2019/10/14 全球购物
献爱心活动总结
2014/05/07 职场文书
组工干部对照检查材料
2014/08/25 职场文书
2015年行风建设工作总结
2015/05/15 职场文书
国博复兴之路观后感
2015/06/02 职场文书
2015年教务主任工作总结
2015/07/22 职场文书