老生常谈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赋值操作方法分享
Mar 23 Python
Python应用03 使用PyQT制作视频播放器实例
Dec 07 Python
Python实现的计算器功能示例
Apr 26 Python
python并发和异步编程实例
Nov 15 Python
对python csv模块配置分隔符和引用符详解
Dec 12 Python
对Python3中dict.keys()转换成list类型的方法详解
Feb 03 Python
python gensim使用word2vec词向量处理中文语料的方法
Jul 05 Python
详解pandas中MultiIndex和对象实际索引不一致问题
Jul 23 Python
python:动态路由的Flask程序代码
Nov 22 Python
python之列表推导式的用法
Nov 29 Python
Django如何重置migration的几种情景
Feb 24 Python
python模块与C和C++动态库相互调用实现过程示例
Nov 02 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模板技术[转]
2007/01/04 PHP
Linux下php5.4启动脚本
2014/08/03 PHP
js+FSO遍历文件夹下文件并显示
2007/03/07 Javascript
JavaScript CSS修改学习第六章 拖拽
2010/02/19 Javascript
Jquery Ajax学习实例5 向WebService发出请求,返回泛型集合数据的异步调用
2010/03/17 Javascript
Enter转换为Tab的小例子(兼容IE,Firefox)
2013/11/14 Javascript
子页向父页传值示例
2013/11/27 Javascript
js中window.open()的所有参数详细解析
2014/01/09 Javascript
JavaScript对象的property属性详解
2014/04/01 Javascript
用js格式化金额可设置保留的小数位数
2014/05/09 Javascript
Javascript学习指南
2014/12/01 Javascript
JavaScript生成随机字符串的方法
2015/03/19 Javascript
javascript函数特点实例分析
2015/05/14 Javascript
AngularJS基础 ng-mousemove 指令简单示例
2016/08/02 Javascript
详解Vue + Vuex 如何使用 vm.$nextTick
2017/11/20 Javascript
p5.js入门教程和基本形状绘制
2018/03/15 Javascript
chosen实现省市区三级联动
2018/08/16 Javascript
vue中v-for循环给标签属性赋值的方法
2018/10/18 Javascript
解决python3捕获cx_oracle抛出的异常错误问题
2018/10/18 Python
使用urllib库的urlretrieve()方法下载网络文件到本地的方法
2018/12/19 Python
Python 运行 shell 获取输出结果的实例
2019/01/07 Python
如何提高python 中for循环的效率
2020/04/15 Python
Python爬取数据并实现可视化代码解析
2020/08/12 Python
Python判断字符串是否为合法标示符操作
2020/09/03 Python
解决python打开https出现certificate verify failed的问题
2020/09/03 Python
html5教程制作简单画板代码分享
2013/12/04 HTML / CSS
大学军训通讯稿
2014/01/13 职场文书
《蒲公英》教学反思
2014/02/28 职场文书
元旦活动感言
2014/03/08 职场文书
高中军训感言600字
2014/03/11 职场文书
2014领导班子四风问题对照检查材料思想汇报
2014/09/21 职场文书
董事长秘书岗位职责
2015/02/13 职场文书
公积金具结保证书
2015/05/11 职场文书
2015年小学语文教师工作总结
2015/10/23 职场文书
css实现文章分割线样式的多种方法总结
2021/04/21 HTML / CSS
React列表栏及购物车组件使用详解
2021/06/28 Javascript