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格式化压缩后的JS文件的方法
Mar 05 Python
python检测远程端口是否打开的方法
Mar 14 Python
Python多线程编程简单介绍
Apr 13 Python
使用Python+Splinter自动刷新抢12306火车票
Jan 03 Python
python将秒数转化为时间格式的实例
Sep 16 Python
python程序控制NAO机器人行走
Apr 29 Python
python读取并定位excel数据坐标系详解
Jun 26 Python
使用 Python 处理 JSON 格式的数据
Jul 22 Python
Python自动化导出zabbix数据并发邮件脚本
Aug 16 Python
python使用docx模块读写docx文件的方法与docx模块常用方法详解
Feb 17 Python
Python迭代器Iterable判断方法解析
Mar 16 Python
Win10下配置tensorflow-gpu的详细教程(无VS2015/2017)
Jul 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
vBulletin HACK----显示话题大小和打开新窗口于论坛索引页
2006/10/09 PHP
php获取服务器端mac和客户端mac的地址支持WIN/LINUX
2014/05/15 PHP
php通过正则表达式记取数据来读取xml的方法
2015/03/09 PHP
PHP安装memcached扩展笔记
2015/05/28 PHP
Yii框架Session与Cookie使用方法示例
2019/10/14 PHP
PHP中isset、empty的用法与区别示例详解
2020/11/05 PHP
PHP7新增函数
2021/03/09 PHP
event.srcElement+表格应用
2006/08/29 Javascript
使用新的消息弹出框blackbirdjs
2008/10/16 Javascript
javaScript 判断字符串是否为数字的简单方法
2009/07/25 Javascript
获取URL地址中的文件名和参数的javascript代码
2009/09/02 Javascript
学习ExtJS table布局
2009/10/08 Javascript
Js使用WScript.Shell对象执行.bat文件和cmd命令
2014/12/18 Javascript
javascript如何创建对象
2016/08/29 Javascript
解决html input验证只能输入数字,不能输入其他的问题
2017/07/21 Javascript
基于jQuery对象和DOM对象和字符串之间的转化实例
2017/08/08 jQuery
详解webpack打包时排除其中一个css、js文件或单独打包一个css、js文件(两种方法)
2018/10/26 Javascript
echarts大屏字体自适应的方法步骤
2019/07/12 Javascript
js实现双人五子棋小游戏
2020/05/28 Javascript
[02:23]2016国际邀请赛中国区预选赛wings晋级之路
2016/06/29 DOTA
Win7 64位下python3.6.5安装配置图文教程
2020/10/27 Python
python getpass实现密文实例详解
2019/09/24 Python
python集合常见运算案例解析
2019/10/17 Python
详解mac python+selenium+Chrome 简单案例
2019/11/08 Python
Pytorch GPU显存充足却显示out of memory的解决方式
2020/01/13 Python
python实现按键精灵找色点击功能教程,使用pywin32和Pillow库
2020/06/04 Python
家乐福巴西网上超市:Carrefour巴西
2016/10/31 全球购物
比利时网上药店: Drogisterij.net
2017/03/17 全球购物
GafasWorld哥伦比亚:网上购买眼镜
2017/11/28 全球购物
澳大利亚手表品牌:Time IV Change
2018/10/06 全球购物
中间件分为哪几类
2012/03/14 面试题
超市国庆节促销方案
2014/02/20 职场文书
结婚保证书
2015/01/16 职场文书
煤矿安全保证书
2015/02/27 职场文书
MySQL悲观锁与乐观锁的实现方案
2021/11/02 MySQL
SQL Server中锁的用法
2022/05/20 SQL Server