老生常谈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中获取网页状态码的两个方法
Nov 03 Python
Python3读取UTF-8文件及统计文件行数的方法
May 22 Python
为Python的Tornado框架配置使用Jinja2模板引擎的方法
Jun 30 Python
python处理Excel xlrd的简单使用
Sep 12 Python
TensorFlow的权值更新方法
Jun 14 Python
Python实现识别图片内容的方法分析
Jul 11 Python
Windows下python3.6.4安装教程
Jul 31 Python
利用selenium爬虫抓取数据的基础教程
Jun 10 Python
python中的split()函数和os.path.split()函数使用详解
Dec 21 Python
基于Python计算圆周率pi代码实例
Mar 25 Python
Django配置Bootstrap, js实现过程详解
Oct 13 Python
装上这 14 个插件后,PyCharm 真的是无敌的存在
Jan 11 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
《Re:从零开始的异世界生活》剧情体验,手游新作定名
2020/04/09 日漫
免费手机号码归属地API查询接口和PHP使用实例分享
2014/04/10 PHP
PHP和javascript常用正则表达式及用法实例
2014/07/01 PHP
异步加载技术实现当滚动条到最底部的瀑布流效果
2014/09/16 PHP
php实现的任意进制互转类分享
2015/07/07 PHP
PHPExcel笔记, mpdf导出
2016/05/03 PHP
php记录搜索引擎爬行记录的实现代码
2018/03/02 PHP
PHP中“=>
2019/03/01 PHP
利用浏览器全屏api实现js全屏
2014/01/16 Javascript
Javascript+CSS实现影像卷帘效果思路及代码
2014/10/20 Javascript
在JavaScript中访问字符串的子串
2015/07/07 Javascript
jQuery实现向下滑出的平滑下拉菜单效果
2015/08/21 Javascript
jQuery解析json数据实例分析
2015/11/24 Javascript
javascript实现一个网页加载进度loading
2017/01/04 Javascript
BootStrap模态框不垂直居中的解决方法
2017/10/19 Javascript
Bootstrap实现可折叠分组侧边导航菜单
2018/03/07 Javascript
移动端 Vue+Vant 的Uploader 实现上传、压缩、旋转图片功能
2019/06/10 Javascript
加速vue组件渲染之性能优化
2020/04/09 Javascript
Python读取图片为16进制表示简单代码
2018/01/19 Python
Python反转序列的方法实例分析
2018/03/21 Python
python实现微信机器人: 登录微信、消息接收、自动回复功能
2019/04/29 Python
Pyecharts绘制全球流向图的示例代码
2020/01/08 Python
对Python中 \r, \n, \r\n的彻底理解
2020/03/06 Python
简单了解python shutil模块原理及使用方法
2020/04/28 Python
Django 解决model 反向引用中的related_name问题
2020/05/19 Python
python爬虫爬取图片的简单代码
2021/01/18 Python
Ibatis中如何提高SQL Map的性能
2013/05/11 面试题
酒店应聘自荐信
2013/11/09 职场文书
工程监理应届生求职信
2013/11/09 职场文书
简历中求职的个人自我评价
2013/12/03 职场文书
监察局领导班子四风问题整改措施思想汇报
2014/10/05 职场文书
2014年挂职干部工作总结
2014/12/06 职场文书
2014小学语文教学工作总结
2014/12/17 职场文书
小学语文继续教育研修日志
2015/11/13 职场文书
2019年个人工作总结范文(3篇)
2019/08/27 职场文书
Go归并排序算法的实现方法
2022/04/06 Golang