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测试驱动开发实例
Oct 08 Python
Python字符和字符值(ASCII或Unicode码值)转换方法
May 21 Python
详解python如何在django中为用户模型添加自定义权限
Oct 15 Python
如何基于Python获取图片的物理尺寸
Nov 25 Python
python pygame实现滚动横版射击游戏城市之战
Nov 25 Python
Python 模拟生成动态产生验证码图片的方法
Feb 01 Python
Python datetime 格式化 明天,昨天实例
Mar 02 Python
简单了解python shutil模块原理及使用方法
Apr 28 Python
virtualenv介绍及简明教程
Jun 23 Python
Python装饰器如何实现修复过程解析
Sep 05 Python
Python中BeautifulSoup通过查找Id获取元素信息
Dec 07 Python
只需要这一行代码就能让python计算速度提高十倍
May 24 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防盗链的常用方法小结
2010/07/02 PHP
基于MySQL体系结构的分析
2013/05/02 PHP
在windows平台上构建自己的PHP实现方法(仅适用于php5.2)
2013/07/05 PHP
php管理nginx虚拟主机shell脚本实例
2014/11/19 PHP
php实现的xml操作类
2016/01/15 PHP
php+mysql+jquery实现日历签到功能
2017/02/27 PHP
PHP+JQUERY操作JSON实例
2017/03/23 PHP
基于JQUERY的两个ListBox子项互相调整的实现代码
2011/05/07 Javascript
三级下拉菜单的js实现代码
2011/05/23 Javascript
推荐11款jQuery开发的复选框和单选框美化插件
2011/08/02 Javascript
JavaScript String.replace函数参数实例说明
2013/06/06 Javascript
js 显示base64编码的二进制流网页图片
2014/04/04 Javascript
初识Node.js
2014/09/03 Javascript
HTML5使用DeviceOrientation实现摇一摇功能
2015/06/05 Javascript
实例解析JS布尔对象的toString()方法和valueOf()方法
2015/10/25 Javascript
jQuery基础_入门必看知识点
2016/07/04 Javascript
AngularJS通过$http和服务器通信详解
2016/09/21 Javascript
AngularJS表单基本操作
2017/01/09 Javascript
JavaScript简单生成 N~M 之间随机数的方法
2017/01/13 Javascript
Nodejs实现短信验证码功能
2017/02/09 NodeJs
vue2的todolist入门小项目的详细解析
2017/05/11 Javascript
基于jQuery实现的Ajax 验证用户名唯一性实例代码
2017/06/28 jQuery
Node4-5静态资源服务器实战以及优化压缩文件实例内容
2019/08/29 Javascript
[01:45]2014DOTA2 TI预选赛预选赛 大神专访第二弹!
2014/05/20 DOTA
使用grappelli为django admin后台添加模板
2014/11/18 Python
浅谈tensorflow中几个随机函数的用法
2018/07/27 Python
Python面向对象基础入门之设置对象属性
2018/12/11 Python
python 画出使用分类器得到的决策边界
2019/08/21 Python
python elasticsearch环境搭建详解
2019/09/02 Python
Python TCPServer 多线程多客户端通信的实现
2019/12/31 Python
Python搭建Keras CNN模型破解网站验证码的实现
2020/04/07 Python
python同时遍历两个list用法说明
2020/05/02 Python
Python常用类型转换实现代码实例
2020/07/28 Python
eBay瑞士购物网站:eBay.ch
2018/12/24 全球购物
ghd法国官方网站:英国最受欢迎的美发工具品牌
2019/04/18 全球购物
六一亲子活动感想
2015/08/07 职场文书