老生常谈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基础教程之字典操作详解
Mar 25 Python
python+selenium识别验证码并登录的示例代码
Dec 21 Python
python如何通过实例方法名字调用方法
Mar 21 Python
Python Unittest自动化单元测试框架详解
Apr 04 Python
详解Python中的动态属性和特性
Apr 07 Python
Python占用的内存优化教程
Jul 28 Python
python使用正则表达式去除中文文本多余空格,保留英文之间空格方法详解
Feb 11 Python
Python列表如何更新值
May 27 Python
python3+opencv 使用灰度直方图来判断图片的亮暗操作
Jun 02 Python
改变 Python 中线程执行顺序的方法
Sep 24 Python
解决Python 写文件报错TypeError的问题
Oct 23 Python
python基于scrapy爬取京东笔记本电脑数据并进行简单处理和分析
Apr 14 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
计算2000年01月01日起到指定日的天数
2006/10/09 PHP
php实现session自定义会话处理器的方法
2015/01/27 PHP
PHP中预定义的6种接口介绍
2015/05/12 PHP
PHP中的Session对象如何使用
2015/09/25 PHP
PHP模板引擎Smarty自定义变量调解器用法
2016/04/11 PHP
PHP利用超级全局变量$_GET来接收表单数据的实例
2016/11/05 PHP
PHP实现可添加水印与生成缩略图的图片处理工具类
2018/01/16 PHP
PHP 对象继承原理与简单用法示例
2020/04/21 PHP
Javascript 日期处理之时区问题
2009/10/08 Javascript
JavaScript是否可实现多线程  深入理解JavaScript定时机制
2009/12/22 Javascript
Package.js  现代化的JavaScript项目make工具
2012/05/23 Javascript
js 获取和设置css3 属性值的实现方法
2013/05/06 Javascript
js点击button按钮跳转到另一个新页面
2014/10/10 Javascript
nodejs实现HTTPS发起POST请求
2015/04/23 NodeJs
jQuery实现页面内锚点平滑跳转特效的方法总结
2015/05/11 Javascript
Vue学习笔记进阶篇之vue-cli安装及介绍
2017/07/18 Javascript
JS实现快速比较两个字符串中包含有相同数字的方法
2017/09/11 Javascript
微信小程序canvas实现刮刮乐效果
2018/07/09 Javascript
微信小程序封装的HTTP请求示例【附升级版】
2019/05/11 Javascript
微信小程序云开发 生成带参小程序码流程
2019/05/18 Javascript
使用vue自定义指令开发表单验证插件validate.js
2019/05/23 Javascript
jQuery实现提交表单时不提交隐藏div中input的方法
2019/10/08 jQuery
Python解析nginx日志文件
2015/05/11 Python
Python判断文件和字符串编码类型的实例
2017/12/21 Python
python合并同类型excel表格的方法
2018/04/01 Python
学Python 3的理由和必要性
2019/11/19 Python
python3中sys.argv的实例用法
2020/04/24 Python
python分布式爬虫中消息队列知识点详解
2020/11/26 Python
美国名牌香水折扣网站:Hottperfume
2021/02/10 全球购物
EM Cosmetics官网:由彩妆大神Michelle Phan创办的独立品牌
2020/04/27 全球购物
Nike俄罗斯官方网站:Nike RU
2021/03/05 全球购物
求职信的最佳写作思路
2014/02/01 职场文书
《毛主席在花山》教学反思
2014/04/20 职场文书
2019学生会干事辞职信
2019/06/27 职场文书
Go缓冲channel和非缓冲channel的区别说明
2021/04/25 Golang
CSS的class与id常用的命名规则
2021/05/18 HTML / CSS