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分析nignx访问日志脚本分享
Feb 26 Python
Python读取properties配置文件操作示例
Mar 29 Python
Python3匿名函数lambda介绍与使用示例
May 18 Python
python自定义函数实现最大值的输出方法
Jul 09 Python
pycharm中显示CSS提示的知识点总结
Jul 29 Python
Python2和3字符编码的区别知识点整理
Aug 08 Python
tensorflow使用L2 regularization正则化修正overfitting过拟合方式
May 22 Python
Python二元算术运算常用方法解析
Sep 15 Python
python进行二次方程式计算的实例讲解
Dec 06 Python
python编写函数注意事项总结
Mar 29 Python
Python实战之用tkinter库做一个鼠标模拟点击器
Apr 27 Python
聊聊Python中关于a=[[]]*3的反思
Jun 02 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
CI框架学习笔记(二) -入口文件index.php
2014/10/27 PHP
在Mac OS上自行编译安装Apache服务器和PHP解释器
2015/12/24 PHP
随鼠标上下滚动的jquery代码
2013/12/05 Javascript
jQuery淡入淡出元素让其效果更为生动
2014/09/01 Javascript
JavaScript中instanceof运算符的使用示例
2016/06/08 Javascript
100多个基础常用JS函数和语法集合大全
2017/02/16 Javascript
js实现功能比较全面的全选和多选
2017/03/02 Javascript
js is_valid_filename验证文件名的函数
2017/07/19 Javascript
如何理解Vue的作用域插槽的实现原理
2017/08/19 Javascript
微信小程序实现刷脸登录
2018/05/25 Javascript
基于jQuery使用Ajax动态执行模糊查询功能
2018/07/05 jQuery
Vue 中文本内容超出规定行数后展开收起的处理的实现方法
2019/04/28 Javascript
[01:04:39]OG vs Mineski 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/18 DOTA
Python进行数据科学工作的简单入门教程
2015/04/01 Python
Python反射的用法实例分析
2018/02/11 Python
Win7 64位下python3.6.5安装配置图文教程
2020/10/27 Python
对python .txt文件读取及数据处理方法总结
2018/04/23 Python
python垃圾回收机制(GC)原理解析
2019/12/30 Python
python GUI库图形界面开发之PyQt5状态栏控件QStatusBar详细使用方法实例
2020/02/28 Python
Django Admin后台添加数据库视图过程解析
2020/04/01 Python
如何解决python多种版本冲突问题
2020/10/13 Python
Django多数据库联用实现方法解析
2020/11/12 Python
Python3+PyCharm+Django+Django REST framework配置与简单开发教程
2021/02/16 Python
Levi’s美国官网:美国著名的牛仔裤品牌
2016/08/19 全球购物
伦敦香水公司:The London Perfume Company
2019/11/13 全球购物
如何防止同一个帐户被多人同时登录
2013/08/01 面试题
机械专业毕业生自荐信
2013/11/02 职场文书
庆祝教师节活动方案
2014/01/31 职场文书
村长贪污检举信
2014/04/04 职场文书
医药销售自我评价200字
2014/09/11 职场文书
2014年乡镇安全生产工作总结
2014/12/02 职场文书
开平碉楼导游词
2015/02/06 职场文书
质量承诺书格式范文
2015/04/28 职场文书
2016年员工政治思想表现评语
2015/12/02 职场文书
Python turtle实现贪吃蛇游戏
2021/06/18 Python
SQL基础查询和LINQ集成化查询
2022/01/18 MySQL