老生常谈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编写检测数据库SA用户的方法
Jul 11 Python
Python素数检测的方法
May 11 Python
Python 多线程的实例详解
Sep 07 Python
Python3.6简单操作Mysql数据库
Sep 12 Python
详解Python如何生成词云的方法
Jun 01 Python
对python创建及引用动态变量名的示例讲解
Nov 10 Python
python 获取一个值在某个区间的指定倍数的值方法
Nov 12 Python
详解Python中is和==的区别
Mar 21 Python
python版DDOS攻击脚本
Jun 12 Python
python3 实现爬取TOP500的音乐信息并存储到mongoDB数据库中
Aug 24 Python
python 多线程死锁问题的解决方案
Aug 25 Python
Python中npy和mat文件的保存与读取
Apr 24 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
mysql 查询指定日期时间内sql语句实现原理与代码
2012/12/16 PHP
基于curl数据采集之单页面并行采集函数get_htmls的使用
2013/04/28 PHP
php实现singleton()单例模式实例
2014/11/06 PHP
完整删除ecshop中获取店铺信息的API
2014/12/24 PHP
PHP Smarty模版简单使用方法
2016/03/30 PHP
PHP记录和读取JSON格式日志文件
2016/07/07 PHP
JavaScript实现动态增加文件域表单
2009/02/12 Javascript
JS正则中的RegExp对象对象
2012/11/07 Javascript
JS打印gridview实现原理及代码
2013/02/05 Javascript
js控制不同的时间段显示不同的css样式的实例代码
2013/11/04 Javascript
编程语言JavaScript简介
2014/10/16 Javascript
AngularJS实现根据变量改变动态加载模板的方法
2016/11/04 Javascript
AngularJs 利用百度地图API 定位当前位置 获取地址信息
2017/01/18 Javascript
AngularJs上传前预览图片的实例代码
2017/01/20 Javascript
js图片延迟加载(Lazyload)三种实现方式
2017/03/01 Javascript
JavaScript之DOM插入更新删除_动力节点Java学院整理
2017/07/03 Javascript
微信小程序中setInterval的使用方法
2017/09/29 Javascript
基于Vue的延迟加载插件vue-view-lazy
2018/05/21 Javascript
javascript实现前端成语点击验证优化
2020/06/24 Javascript
[45:14]Optic vs VP 2018国际邀请赛淘汰赛BO3 第二场 8.24
2018/08/25 DOTA
Python中的with...as用法介绍
2015/05/28 Python
在MAC上搭建python数据分析开发环境
2016/01/26 Python
Python模块结构与布局操作方法实例分析
2017/07/24 Python
python opencv人脸检测提取及保存方法
2018/08/03 Python
Python完成哈夫曼树编码过程及原理详解
2019/07/29 Python
解决python图像处理图像赋值后变为白色的问题
2020/06/04 Python
python3 简单实现组合设计模式
2020/07/02 Python
基于Python实现体育彩票选号器功能代码实例
2020/09/16 Python
80后职场人的职业生涯规划
2014/03/08 职场文书
应聘编辑自荐信范文
2014/03/12 职场文书
班主任寄语大全
2014/04/04 职场文书
个人求职自荐信范文
2014/06/20 职场文书
解放思想大讨论活动心得体会
2014/09/11 职场文书
医院营销工作计划
2015/01/16 职场文书
小学三年级语文教学反思
2016/03/03 职场文书
vue里使用create, mounted调用方法
2022/04/26 Vue.js