老生常谈Python进阶之装饰器


Posted in Python onMay 11, 2017

函数也是对象

要理解Python装饰器,首先要明白在Python中,函数也是一种对象,因此可以把定义函数时的函数名看作是函数对象的一个引用。既然是引用,因此可以将函数赋值给一个变量,也可以把函数作为一个参数传递或返回。同时,函数体中也可以再定义函数。

装饰器本质

可以通过编写一个纯函数的例子来还原装饰器所要做的事。

def decorator(func):
  
  def wrap():
    print("Doing someting before executing func()")
    func()
    print("Doing someting after executing func()")

  return wrap


def fun_test():
  print("func")


fun_test = decorator(fun_test)
fun_test()

# Output:
# Doing someting before executing func()
# func
# Doing someting after executing func()

fun_test所指向的函数的引用传递给decorator()函数

decorator()函数中定义了wrap()子函数,这个子函数会调用通过func引用传递进来的fun_test()函数,并在调用函数的前后做了一些其他的事情

decorator()函数返回内部定义的wrap()函数引用

fun_test接收decorator()返回的函数引用,从而指向了一个新的函数对象

通过fun_test()调用新的函数执行wrap()函数的功能,从而完成了对fun_test()函数的前后装饰

Python中使用装饰器

在Python中可以通过@符号来方便的使用装饰器功能。

def decorator(func):
  
  def wrap():
    print("Doing someting before executing func()")
    func()
    print("Doing someting after executing func()")

  return wrap

@decorator
def fun_test():
  print("func")


fun_test()

# Output:
# Doing someting before executing func()
# func
# Doing someting after executing func()

装饰的功能已经实现了,但是此时执行:

 

print(fun_test.__name__)

# Output:
# wrap

 fun_test.__name__已经变成了wrap,这是应为wrap()函数已经重写了我们函数的名字和注释文档。此时可以通过functools.wraps来解决这个问题。wraps接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。

更规范的写法:

from functools import wraps

def decorator(func):
  @wraps(func)
  def wrap():
    print("Doing someting before executing func()")
    func()
    print("Doing someting after executing func()")

  return wrap


@decorator
def fun_test():
  print("func")


fun_test()
print(fun_test.__name__)

# Output:
# Doing someting before executing func()
# func
# Doing someting after executing func()
# fun_test

带参数的装饰器

通过返回一个包裹函数的函数,可以模仿wraps装饰器,构造出一个带参数的装饰器。

from functools import wraps

def loginfo(info='info1'):
  def loginfo_decorator(func):
    @wraps(func)
    def wrap_func(*args, **kwargs):
      print(func.__name__ + ' was called')
      print('info: %s' % info)
      
      return func(*args, **kwargs)
    return wrap_func
  return loginfo_decorator
  
@loginfo()
def func1():
  pass
  
func1()

# Output:
# func1 was called
# info: info1

@loginfo(info='info2')
def func2():
  pass

func2()
# Output:
# func2 was called
# info: info2

装饰器类

通过编写类的方法也可以实现装饰器,并让装饰器具备继承等面向对象中更实用的特性

首先编写一个装饰器基类:

from functools import wraps

class loginfo:
  def __init__(self, info='info1'):
    self.info = info
    
  def __call__(self, func):
    @wrap
    def wrap_func(*args, **kwargs):
      print(func.__name__ + ' was called')
      print('info: %s' % self.info)
      
      self.after()  # 调用after方法,可以在子类中实现
      return func(*args, **kwargs)
    return wrap_func

  def after(self):
    pass


@loginfo(info='info2')
def func1():
  pass
  
# Output:
# func1 was called
# info: info1

再通过继承loginfo类,扩展装饰器的功能:

class loginfo_after(loginfo):
  def __init__(self, info2='info2', *args, **kwargs):
    self.info2 = info2
    super(loginfo_after, self).__init__(*args, **kwargs)

  def after(self):
    print('after: %s' % self.info2)


@loginfo_after()
def func2():
  pass

func2()
  
# Output:
# func2 was called
# info: info1
# after: info2

以上这篇老生常谈Python进阶之装饰器就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python控制台英汉汉英电子词典
Apr 23 Python
python获得两个数组交集、并集、差集的方法
Mar 27 Python
以windows service方式运行Python程序的方法
Jun 03 Python
老生常谈python之鸭子类和多态
Jun 13 Python
python实现批量注册网站用户的示例
Feb 22 Python
python安装virtualenv虚拟环境步骤图文详解
Sep 18 Python
TENSORFLOW变量作用域(VARIABLE SCOPE)
Jan 10 Python
python通过安装itchat包实现微信自动回复收到的春节祝福
Jan 19 Python
利用OpenCV中对图像数据进行64F和8U转换的方式
Jun 03 Python
PyCharm2020最新激活码+激活码补丁(亲测最新版PyCharm2020.2激活成功)
Nov 25 Python
Python常用base64 md5 aes des crc32加密解密方法汇总
Nov 06 Python
python文件与路径操作神器 pathlib
Apr 01 Python
python 第三方库的安装及pip的使用详解
May 11 #Python
插入排序_Python与PHP的实现版(推荐)
May 11 #Python
Python实现计算两个时间之间相差天数的方法
May 10 #Python
Python开发的实用计算器完整实例
May 10 #Python
Python只用40行代码编写的计算器实例
May 10 #Python
Python实现脚本锁功能(同时只能执行一个脚本)
May 10 #Python
python 3.5下xadmin的使用及修复源码bug
May 10 #Python
You might like
php daodb插入、更新与删除数据
2009/03/19 PHP
mysql_num_rows VS COUNT 效率问题分析
2011/04/23 PHP
PHP利用正则表达式将相对路径转成绝对路径的方法示例
2017/02/28 PHP
JavaScript判断DOM何时加载完毕的技巧
2012/11/11 Javascript
关于jQuery参考实例 1.0 jQuery的哲学
2013/04/07 Javascript
js实现动态加载脚本的方法实例汇总
2015/11/02 Javascript
Javascript实现通过选择周数显示开始日和结束日的实现代码
2016/05/30 Javascript
node+experss实现爬取电影天堂爬虫
2016/11/20 Javascript
AngularJS中run方法的巧妙运用
2017/01/04 Javascript
jQuery NProgress.js加载进度插件的简单使用方法
2018/01/31 jQuery
swiper 自动图片无限轮播实现代码
2018/05/21 Javascript
webpack打包nodejs项目的方法
2018/09/26 NodeJs
使用Angular自定义字段校验指令的方法示例
2019/02/01 Javascript
详解JavaScript的数据类型以及数据类型的转换
2019/04/20 Javascript
微信小程序点餐系统开发常见问题汇总
2019/08/06 Javascript
JavaScript如何实现图片处理与合成
2020/05/29 Javascript
详解Vue3 Teleport 的实践及原理
2020/12/02 Vue.js
Python中使用wxPython开发的一个简易笔记本程序实例
2015/02/08 Python
举例详解Python中threading模块的几个常用方法
2015/06/18 Python
Python自动化测试ConfigParser模块读写配置文件
2016/08/15 Python
python+splinter自动刷新抢票功能
2018/09/25 Python
利用Python求阴影部分的面积实例代码
2018/12/05 Python
Python3 实现串口两进程同时读写
2019/06/12 Python
python argparser的具体使用
2019/11/10 Python
使用Python的Turtle绘制哆啦A梦实例
2019/11/21 Python
使用jupyter notebook将文件保存为Markdown,HTML等文件格式
2020/04/14 Python
Groupon法国官方网站:特卖和网上购物高达-70%
2019/09/02 全球购物
大学生创业感言
2014/01/25 职场文书
《一件运动衫》教学反思
2014/02/19 职场文书
中药学自荐信
2014/06/15 职场文书
电子专业自荐信
2014/07/01 职场文书
数学教育专业求职信
2014/07/22 职场文书
2014年医务科工作总结
2014/12/18 职场文书
企业战略合作意向书
2015/05/08 职场文书
pytorch 权重weight 与 梯度grad 可视化操作
2021/06/05 Python
python人工智能human learn绘图可创建机器学习模型
2021/11/23 Python