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中一些自然语言工具的使用的入门教程
Apr 13 Python
Django自定义分页与bootstrap分页结合
Feb 22 Python
分享一下Python数据分析常用的8款工具
Apr 29 Python
解决Mac安装scrapy失败的问题
Jun 13 Python
利用Pandas读取文件路径或文件名称包含中文的csv文件方法
Jul 04 Python
python使用xlsxwriter实现有向无环图到Excel的转换
Dec 12 Python
python 日期排序的实例代码
Jul 11 Python
python twilio模块实现发送手机短信功能
Aug 02 Python
Numpy 中的矩阵求逆实例
Aug 26 Python
Python argparse模块使用方法解析
Feb 20 Python
python的Jenkins接口调用方式
May 12 Python
keras.layer.input()用法说明
Jun 16 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
php文件怎么打开 如何执行php文件
2011/12/21 PHP
php数组操作之键名比较与差集、交集赋值的方法
2014/11/10 PHP
php中convert_uuencode()与convert_uuencode函数用法实例
2014/11/22 PHP
PHP实现UTF8二进制及明文字符串的转化功能示例
2017/11/20 PHP
php使用redis的有序集合zset实现延迟队列应用示例
2020/02/20 PHP
javascript Array.sort() 跨浏览器下需要考虑的问题
2009/12/07 Javascript
jquery 图片轮换效果
2010/07/29 Javascript
JavaScript中setInterval的用法总结
2013/11/20 Javascript
JavaScript使用Replace进行字符串替换的方法
2015/04/14 Javascript
深入理解JavaScript编程中的同步与异步机制
2015/06/24 Javascript
JQuery日期插件datepicker的使用方法
2016/03/03 Javascript
jQuery向父辈遍历的简单方法
2016/09/18 Javascript
BootStrap表单验证实例代码
2017/01/13 Javascript
微信小程序 scroll-view实现上拉加载与下拉刷新的实例
2017/01/21 Javascript
webpack入门+react环境配置
2017/02/08 Javascript
bootstrap jquery dataTable 异步ajax刷新表格数据的实现方法
2017/02/10 Javascript
jquery.validate.js 多个相同name的处理方式
2017/07/10 jQuery
微信小程序 共用变量值的实现
2017/07/12 Javascript
puppeteer实现html截图的示例代码
2019/01/10 Javascript
vue请求服务器数据后绑定不上的解决方法
2019/10/30 Javascript
Vue设置长时间未操作登录自动到期返回登录页
2020/01/22 Javascript
[45:52]完美世界DOTA2联赛PWL S3 Forest vs INK ICE 第二场 12.09
2020/12/12 DOTA
Python分支结构(switch)操作简介
2018/01/17 Python
Python输入二维数组方法
2018/04/13 Python
tensorflow学习笔记之mnist的卷积神经网络实例
2018/04/15 Python
Python编程图形库之Pillow使用方法讲解
2018/12/28 Python
pycharm打开命令行或Terminal的方法
2019/01/16 Python
正确理解Python中if __name__ == '__main__'
2019/01/24 Python
python2 中 unicode 和 str 之间的转换及与python3 str 的区别
2019/07/25 Python
Python编译成.so文件进行加密后调用的实现
2019/12/23 Python
Python+OpenCV实现将图像转换为二进制格式
2020/01/09 Python
Python调用Windows命令打印文件
2020/02/07 Python
python获取响应某个字段值的3种实现方法
2020/04/30 Python
Shell如何接收变量输入
2016/08/06 面试题
停车位租赁协议书
2014/09/24 职场文书
慰问信格式规范
2015/03/23 职场文书