老生常谈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 django集成cas验证系统
Jul 14 Python
python实现备份目录的方法
Aug 03 Python
Python中条件判断语句的简单使用方法
Aug 21 Python
python学习之编写查询ip程序
Feb 27 Python
python 类详解及简单实例
Mar 24 Python
在python中利用最小二乘拟合二次抛物线函数的方法
Dec 29 Python
Python下简易的单例模式详解
Apr 08 Python
Django 创建新App及其常用命令的实现方法
Aug 04 Python
Python 进程操作之进程间通过队列共享数据,队列Queue简单示例
Oct 11 Python
python 计算积分图和haar特征的实例代码
Nov 20 Python
python实现图片二值化及灰度处理方式
Dec 07 Python
python新手学习可变和不可变对象
Jun 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
PHP APC缓存配置、使用详解
2014/03/06 PHP
PHP实现多文件上传的方法
2015/07/08 PHP
php实现给一张图片加上水印效果
2016/01/02 PHP
完美解决Thinkphp3.2中插入相同数据的问题
2017/08/01 PHP
php7 新增功能实例总结
2020/05/25 PHP
W3C Group的JavaScript1.8 新特性介绍
2009/05/19 Javascript
js操作二级联动实现代码
2010/07/27 Javascript
onclick与listeners的执行先后问题详细解剖
2013/01/07 Javascript
关于JavaScript 原型链的一点个人理解
2016/07/31 Javascript
使用ionic切换页面卡顿的解决方法
2016/12/16 Javascript
angular-ui-sortable实现可拖拽排序列表
2016/12/28 Javascript
jQuery实现贪吃蛇小游戏(附源码下载)
2017/03/04 Javascript
JQuery.dataTables表格插件添加跳转到指定页
2017/06/09 jQuery
微信小程序 上传头像的实例详解
2017/10/27 Javascript
Vue 让元素抖动/摆动起来的实现代码
2018/05/31 Javascript
vue使用混入定义全局变量、函数、筛选器的实例代码
2019/07/29 Javascript
VUE实现图片验证码功能
2020/11/18 Javascript
JavaScript实现切换多张图片
2021/01/27 Javascript
Python生成验证码实例
2014/08/21 Python
python修改字典内key对应值的方法
2015/07/11 Python
教大家玩转Python字符串处理的七种技巧
2017/03/31 Python
PyQt5主窗口动态加载Widget实例代码
2018/02/07 Python
python实现根据指定字符截取对应的行的内容方法
2018/10/23 Python
用Python解决x的n次方问题
2019/02/08 Python
python操作文件的参数整理
2019/06/11 Python
django项目简单调取百度翻译接口的方法
2019/08/06 Python
python实现飞船大战
2020/04/24 Python
详解Django自定义图片和文件上传路径(upload_to)的2种方式
2020/12/01 Python
如何开发安全的AJAX应用
2014/03/26 面试题
名人演讲稿范文
2013/12/28 职场文书
财务方面个人工作的自我评价
2013/12/28 职场文书
党员创先争优心得体会
2014/09/11 职场文书
2014领导班子专题民主生活会对照检查材料思想汇报
2014/09/23 职场文书
党员年终个人总结
2015/02/14 职场文书
施工安全保证书
2015/05/09 职场文书
Win10 和 Win11可以共存吗? win10/11产品生命周期/服务更新介绍
2021/11/21 数码科技