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控制台显示时钟的示例
Feb 24 Python
Python多进程机制实例详解
Jul 02 Python
使用pandas中的DataFrame数据绘制柱状图的方法
Apr 10 Python
Flask框架实现给视图函数增加装饰器操作示例
Jul 16 Python
Django 限制用户访问频率的中间件的实现
Aug 23 Python
用pycharm开发django项目示例代码
Oct 24 Python
Python中is和==的区别详解
Nov 15 Python
详解Python3序列赋值、序列解包
May 14 Python
python误差棒图errorbar()函数实例解析
Feb 11 Python
Python 实现 T00ls 自动签到脚本代码(邮件+钉钉通知)
Jul 06 Python
Python定时任务框架APScheduler原理及常用代码
Oct 05 Python
Python requests HTTP验证登录实现流程
Nov 05 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设计模式 Proxy (代理模式)
2011/06/26 PHP
PHP图片验证码制作实现分享(全)
2012/05/10 PHP
php字符比较函数similar_text、strnatcmp与strcasecmp用法分析
2014/11/18 PHP
php实现通用的从数据库表读取数据到数组的函数实例
2015/03/21 PHP
php实现仿写CodeIgniter的购物车类
2015/07/29 PHP
关于ThinkPHP中的异常处理详解
2018/05/11 PHP
IE6-IE9不支持table.innerHTML的解决方法分享
2012/09/14 Javascript
Highcharts 非常实用的Javascript统计图demo示例
2013/07/03 Javascript
JavaScript对象的property属性详解
2014/04/01 Javascript
jQuery使用toggleClass方法动态添加删除Class样式的方法
2015/03/26 Javascript
javascript动态创建链接的方法
2015/05/13 Javascript
js实现文件上传表单域美化特效
2015/11/02 Javascript
jstree的简单实例
2016/12/01 Javascript
bootstrap下拉菜单使用方法解析
2017/01/13 Javascript
Angular2-primeNG文件上传模块FileUpload使用详解
2017/01/14 Javascript
JS与jQuery实现子窗口获取父窗口元素值的方法
2017/04/17 jQuery
JavaScript切换搜索引擎的导航网页搜索框实例代码
2017/06/11 Javascript
使用 jQuery 实现表单验证功能
2017/07/05 jQuery
ReactNative踩坑之配置调试端口的解决方法
2017/07/28 Javascript
JavaScript中Hoisting详解 (变量提升与函数声明提升)
2017/08/18 Javascript
使用nodeJs来安装less及编译less文件为css文件的方法
2017/11/20 NodeJs
SSM+layUI 根据登录信息显示不同的页面方法
2019/09/20 Javascript
python双向链表实现实例代码
2013/11/21 Python
Python3爬虫之urllib携带cookie爬取网页的方法
2018/12/28 Python
pycharm 安装JPype的教程
2019/08/08 Python
python3下pygame如何实现显示中文
2020/01/11 Python
Python3中的f-Strings增强版字符串格式化方法
2020/03/04 Python
英国计算机产品零售商:Novatech(定制个人电脑、笔记本电脑、工作站和服务器)
2018/01/28 全球购物
英国乡村时尚和宠物用品专家:Pet & Country
2018/07/02 全球购物
澳大利亚领先的男装零售连锁店:Lowes
2020/08/07 全球购物
python+selenium小米商城红米K40手机自动抢购的示例代码
2021/03/24 Python
大学毕业感言50字
2014/02/07 职场文书
副总经理党的群众路线教育实践活动个人对照检查材料思想汇报
2014/10/06 职场文书
工程部岗位职责
2015/02/10 职场文书
详解Oracle块修改跟踪功能
2021/11/07 Oracle
win10以太网连接不上怎么办?Win10连接以太网详细教程
2022/04/08 数码科技