老生常谈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 05 Python
Python中__name__的使用实例
Apr 14 Python
python实现爬虫统计学校BBS男女比例之多线程爬虫(二)
Dec 31 Python
Random 在 Python 中的使用方法
Aug 09 Python
python实现自动化上线脚本的示例
Jul 01 Python
Django使用中间件解决前后端同源策略问题
Sep 02 Python
Python协程操作之gevent(yield阻塞,greenlet),协程实现多任务(有规律的交替协作执行)用法详解
Oct 14 Python
python实现加密的方式总结
Jan 19 Python
pytorch dataloader 取batch_size时候出现bug的解决方式
Feb 20 Python
Python编程快速上手——强口令检测算法案例分析
Feb 29 Python
django中嵌套的try-except实例
May 21 Python
python matplotlib工具栏源码探析三之添加、删除自定义工具项的案例详解
Feb 25 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 生成的XML以FLASH获取为乱码终极解决
2009/08/07 PHP
PHP句法规则详解 入门学习
2011/11/09 PHP
PHP设计模式之结构模式的深入解析
2013/06/13 PHP
php抽象类使用要点与注意事项分析
2015/02/09 PHP
PHP面向对象自动加载机制原理与用法分析
2016/10/14 PHP
php 字符串中是否包含指定字符串的多种方法
2018/04/12 PHP
laravel5.6 框架操作数据 Eloquent ORM用法示例
2020/01/26 PHP
PHP程序员简单的开展服务治理架构操作详解(一)
2020/05/14 PHP
使用TextRange获取输入框中光标的位
2006/10/14 Javascript
JAVASCRIPT函数作用域和提前声明 分享
2013/08/22 Javascript
简单实现JS对dom操作封装
2015/12/02 Javascript
jQuery实现的表格展开伸缩效果实例
2016/09/07 Javascript
JS实现给json数组动态赋值的方法示例
2020/03/19 Javascript
javascript计算对象长度的方法
2017/10/25 Javascript
简单易扩展可控性强的Jquery转盘抽奖程序
2019/03/16 jQuery
微信小程序云开发 搭建一个管理小程序
2019/05/17 Javascript
Vue项目打包压缩的实现(让页面更快响应)
2020/03/10 Javascript
python进阶教程之循环相关函数range、enumerate、zip
2014/08/30 Python
Python描述器descriptor详解
2015/02/03 Python
Windows下Python的Django框架环境部署及应用编写入门
2016/03/10 Python
致Python初学者 Anaconda入门使用指南完整版
2018/04/05 Python
python 3调用百度OCR API实现剪贴板文字识别
2018/09/04 Python
python调用matlab的m自定义函数方法
2019/02/18 Python
python操作excel让工作自动化
2019/08/09 Python
Python中顺序表原理与实现方法详解
2019/12/03 Python
python实现控制台输出颜色
2021/03/02 Python
Python .py生成.pyd文件并打包.exe 的注意事项说明
2021/03/04 Python
美国球迷装备的第一来源:FOCO
2020/07/03 全球购物
英国Lookfantastic中文网站:护肤品美妆美发购物(英国直邮)
2020/04/27 全球购物
自动化系在校本科生求职信
2013/10/23 职场文书
《听鱼说话》教学反思
2014/02/15 职场文书
合法的离婚协议书范本
2014/10/23 职场文书
大学生毕业个人总结
2015/02/15 职场文书
对领导班子的意见和建议
2015/06/08 职场文书
2016年学校禁毒宣传活动工作总结
2016/04/05 职场文书
opencv用VS2013调试时用Image Watch插件查看图片
2021/07/26 Python