老生常谈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合并文本文件示例
Feb 07 Python
python字符串排序方法
Aug 29 Python
python基于windows平台锁定键盘输入的方法
Mar 05 Python
Python 3实战爬虫之爬取京东图书的图片详解
Oct 09 Python
对python中array.sum(axis=?)的用法介绍
Jun 28 Python
详解Python requests 超时和重试的方法
Dec 18 Python
Python获取网段内ping通IP的方法
Jan 31 Python
python双端队列原理、实现与使用方法分析
Nov 27 Python
Python爬虫设置ip代理过程解析
Jul 20 Python
如何将anaconda安装配置的mmdetection环境离线拷贝到另一台电脑
Oct 15 Python
关于Python错误重试方法总结
Jan 03 Python
基于Python-Pycharm实现的猴子摘桃小游戏(源代码)
Feb 20 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
丧钟首部独立剧集《丧钟:骑士与龙》北美正式开播,场面血腥
2020/04/09 欧美动漫
php循环检测目录是否存在并创建(循环创建目录)
2011/01/06 PHP
php延迟静态绑定实例分析
2015/02/08 PHP
PHP实现动态web服务器方法
2015/07/29 PHP
简单了解PHP编程中数组的指针的使用
2015/11/30 PHP
PHP简单判断iPhone、iPad、Android及PC设备的方法
2016/10/11 PHP
分析js闭包引起的事件注册问题
2016/03/29 Javascript
深入理解jQuery中的事件冒泡
2016/05/24 Javascript
jQuery实现鼠标选中文字后弹出提示窗口效果【附demo源码】
2016/09/05 Javascript
JavaScript拖动层Div代码
2017/03/01 Javascript
原生JS实现的简单小钟表功能示例
2018/08/30 Javascript
Vue表单绑定的实例代码(单选按钮,选择框(单选时,多选时,用 v-for 渲染的动态选项)
2019/05/13 Javascript
ant-design-vue 快速避坑指南(推荐)
2020/01/21 Javascript
原生js+ajax分页组件
2020/01/30 Javascript
Openlayers学习之加载鹰眼控件
2020/09/28 Javascript
vue图片裁剪插件vue-cropper使用方法详解
2020/12/16 Vue.js
[01:13]2014DOTA2西雅图邀请赛 舌尖上的TI4
2014/07/08 DOTA
[37:21]完美世界DOTA2联赛PWL S2 Inki vs Magma 第二场 11.22
2020/11/24 DOTA
tornado框架blog模块分析与使用
2013/11/21 Python
Python中使用wxPython开发的一个简易笔记本程序实例
2015/02/08 Python
基于python3 类的属性、方法、封装、继承实例讲解
2017/09/19 Python
详解python里使用正则表达式的分组命名方式
2017/10/24 Python
python3下使用cv2.imwrite存储带有中文路径图片的方法
2018/05/10 Python
python网络编程:socketserver的基本使用方法实例分析
2020/04/09 Python
String是最基本的数据类型吗?
2013/06/13 面试题
预备党员入党思想汇报
2014/01/04 职场文书
春节请假条
2014/04/11 职场文书
建筑工程质量通病防治方案
2014/06/08 职场文书
高中生国庆节演讲稿范文2014
2014/09/21 职场文书
党员个人剖析材料2014
2014/10/08 职场文书
公司行政助理岗位职责
2015/04/11 职场文书
电力工程合作意向书
2015/05/11 职场文书
交通事故被告答辩状
2015/05/22 职场文书
郭明义观后感
2015/06/08 职场文书
Java数组详细介绍及相关工具类
2022/04/14 Java/Android
Spring Boot接口定义和全局异常统一处理
2022/04/20 Java/Android