老生常谈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)
Aug 31 Python
[原创]Python入门教程4. 元组基本操作
Oct 31 Python
python爬虫URL重试机制的实现方法(python2.7以及python3.5)
Dec 18 Python
浅谈Python中eval的强大与危害
Mar 13 Python
Python 20行简单实现有道在线翻译的详解
May 15 Python
Python 3 实现定义跨模块的全局变量和使用教程
Jul 07 Python
python中的&&及||的实现示例
Aug 07 Python
Win10 安装PyCharm2019.1.1(图文教程)
Sep 29 Python
python中return的返回和执行实例
Dec 24 Python
python实现删除列表中某个元素的3种方法
Jan 15 Python
PyQt5.6+pycharm配置以及pyinstaller生成exe(小白教程)
Jun 02 Python
yolov5返回坐标的方法实例
Mar 17 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
JS 的应用开发初探(mootools)
2009/12/19 Javascript
JavaScript词法作用域与调用对象深入理解
2012/11/29 Javascript
使用Js让Html中特殊字符不被转义
2013/11/05 Javascript
js获取页面传来参数的方法
2014/09/06 Javascript
JavaScript实现仿新浪微博大厅和腾讯微博首页滚动特效源码
2015/09/15 Javascript
javascript鼠标滑过显示二级菜单特效
2020/11/18 Javascript
JS实现倒计时(天数、时、分、秒)
2016/11/16 Javascript
Javascript中构造函数要注意的一些坑
2017/01/23 Javascript
Js利用Canvas实现图片压缩功能
2017/09/13 Javascript
Bootstrap的aria-label和aria-labelledby属性实例详解
2018/11/02 Javascript
解决ie11 SCRIPT5011:不能执行已释放Script的代码问题
2019/05/05 Javascript
详解用js代码触发dom事件的实现方案
2020/06/10 Javascript
vue 解决兄弟组件、跨组件深层次的通信操作
2020/07/27 Javascript
js实现点击上传图片并设为模糊背景
2020/08/02 Javascript
微信小程序实现电影App导航和轮播
2020/11/30 Javascript
[01:03:47]VP vs NewBee Supermajor 胜者组 BO3 第一场 6.5
2018/06/06 DOTA
[01:10:27]DOTA2-DPC中国联赛正赛 SAG vs XG BO3 第二场 3月5日
2021/03/11 DOTA
python打开文件并获取文件相关属性的方法
2015/04/23 Python
python获取一组数据里最大值max函数用法实例
2015/05/26 Python
python基础之入门必看操作
2017/07/26 Python
Python获取昨天、今天、明天开始、结束时间戳的方法
2018/06/01 Python
kaggle+mnist实现手写字体识别
2018/07/26 Python
Python网络爬虫之爬取微博热搜
2019/04/18 Python
Python爬虫:将headers请求头字符串转为字典的方法
2019/08/21 Python
Python生成器常见问题及解决方案
2020/03/21 Python
Python自动登录QQ的实现示例
2020/08/28 Python
详解python中的异常捕获
2020/12/15 Python
Python图像处理之膨胀与腐蚀的操作
2021/02/07 Python
Pretty Little Thing爱尔兰:时尚女性服饰
2017/03/27 全球购物
Reebok官方旗舰店:美国知名健身品牌锐步
2019/01/07 全球购物
施华洛世奇新加坡官网:SWAROVSKI新加坡
2020/10/06 全球购物
诉讼授权委托书范本
2014/10/05 职场文书
2015小学五年级班主任工作总结
2015/05/21 职场文书
Axios代理配置及封装响应拦截处理方式
2022/04/07 Vue.js
python数据处理之Pandas类型转换
2022/04/28 Python
JS开发前端团队展示控制器来为成员引流
2022/08/14 Javascript