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中的__builtins__内建对象
Jun 21 Python
Python cookbook(数据结构与算法)同时对数据做转换和换算处理操作示例
Mar 23 Python
django admin 后台实现三级联动的示例代码
Jun 22 Python
python3爬虫学习之数据存储txt的案例详解
Apr 24 Python
python批量下载抖音视频
Jun 17 Python
对PyQt5基本窗口控件 QMainWindow的使用详解
Jun 19 Python
python选取特定列 pandas iloc,loc,icol的使用详解(列切片及行切片)
Aug 06 Python
Python Web框架之Django框架Model基础详解
Aug 16 Python
python实现文件批量编码转换及注意事项
Oct 14 Python
python实现超市管理系统(后台管理)
Oct 25 Python
Django框架之中间件MiddleWare的实现
Dec 30 Python
Python yield生成器和return对比代码实例
Apr 20 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的MVC模式实现原理分析(一相简单的MVC框架范例)
2014/04/29 PHP
Yii 2.0实现联表查询加搜索分页的方法示例
2017/08/02 PHP
Html中JS脚本执行顺序简单举例说明
2010/06/19 Javascript
Javascript代码在页面加载时的执行顺序介绍
2013/05/03 Javascript
jquery实现智能感知连接外网搜索
2013/05/21 Javascript
javascript中的throttle和debounce浅析
2014/06/06 Javascript
JavaScript编程中容易出BUG的几点小知识
2015/01/31 Javascript
举例讲解jQuery对DOM元素的向上遍历、向下遍历和水平遍历
2016/07/07 Javascript
详解AngularJS中ng-src指令的使用
2016/09/07 Javascript
Node.js与MySQL交互操作及其注意事项
2016/10/05 Javascript
实例解析jQuery中如何取消后续执行内容
2016/12/01 Javascript
基于Vue如何封装分页组件
2016/12/16 Javascript
Vue + Vue-router 同名路由切换数据不更新的方法
2017/11/20 Javascript
nodejs实现解析xml字符串为对象的方法示例
2018/03/14 NodeJs
如何使用Node.js爬取任意网页资源并输出PDF文件到本地
2019/06/17 Javascript
浅谈vue项目,访问路径#号的问题
2020/08/14 Javascript
python操作excel的包(openpyxl、xlsxwriter)
2018/06/11 Python
关于python2 csv写入空白行的问题
2018/06/22 Python
python与caffe改变通道顺序的方法
2018/08/04 Python
python将pandas datarame保存为txt文件的实例
2019/02/12 Python
Python实现使用request模块下载图片demo示例
2019/05/24 Python
Python 爬虫的原理
2020/07/30 Python
利用Python实现字幕挂载(把字幕文件与视频合并)思路详解
2020/10/21 Python
简单总结CSS3中视窗单位Viewport的常见用法
2016/02/04 HTML / CSS
小学教师的个人自我鉴定
2013/10/24 职场文书
材料物理专业个人求职信
2013/12/15 职场文书
个人自我剖析材料
2014/02/07 职场文书
更夫岗位责任制
2014/02/11 职场文书
优秀教师推荐材料
2014/12/16 职场文书
老人节主持词
2015/07/04 职场文书
学长教您写论文:经验总结
2019/07/09 职场文书
七年级作文之雪景
2019/11/18 职场文书
低门槛开发iOS、Android、小程序应用的前端框架详解
2021/10/16 Javascript
Mysql 如何合理地统计一个数据库里的所有表的数据量
2022/04/18 MySQL
微信告警的zabbix监控系统 监控整个NGINX集群
2022/04/18 Servers
nginx七层负载均衡配置详解
2022/07/15 Servers