老生常谈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 相关文章推荐
使用PyV8在Python爬虫中执行js代码
Feb 16 Python
使用XML库的方式,实现RPC通信的方法(推荐)
Jun 14 Python
python基础教程项目三之万能的XML
Apr 02 Python
对Python的多进程锁的使用方法详解
Feb 18 Python
用Python实现将一张图片分成9宫格的示例
Jul 05 Python
Python基于Socket实现简单聊天室
Feb 17 Python
Pytorch 使用不同版本的cuda的方法步骤
Apr 02 Python
pycharm第三方库安装失败的问题及解决经验分享
May 09 Python
Python手动或自动协程操作方法解析
Jun 22 Python
Pytorch 如何实现常用正则化
May 27 Python
Django使用echarts进行可视化展示的实践
Jun 10 Python
Python 匹配文本并在其上一行追加文本
May 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中文字符截取防乱码
2008/03/28 PHP
谈谈新手如何学习PHP 默默经典版本
2009/08/04 PHP
10个实用的PHP代码片段
2011/09/02 PHP
ThinkPHP实现事务回滚示例代码
2014/06/23 PHP
PHP性能分析工具XHProf安装使用教程
2015/05/13 PHP
php的无刷新操作实现方法分析
2020/02/28 PHP
Javascript无阻塞加载具体方式
2013/06/28 Javascript
node.js中的console用法总结
2014/12/15 Javascript
JS清除选择内容的方法
2015/01/29 Javascript
DEDECMS如何为文章添加HOT NEW标志图片
2015/08/14 Javascript
Node.js模块封装及使用方法
2016/03/06 Javascript
jQuery AJAX timeout 超时问题详解
2016/06/21 Javascript
AngularJS基础 ng-keypress 指令简单示例
2016/08/02 Javascript
js实现类bootstrap模态框动画
2017/02/07 Javascript
彻底解决 webpack 打包文件体积过大问题
2017/07/07 Javascript
详解JS数据类型的值拷贝函数(深拷贝)
2017/07/13 Javascript
微信小程序实时聊天WebSocket
2018/07/05 Javascript
详解javascript对数组和json数组的操作
2019/04/15 Javascript
基于JavaScript实现轮播图效果
2021/01/02 Javascript
[04:39]显微镜下的DOTA2第十三期—Pis卡尔个人秀
2014/04/04 DOTA
简单介绍Python中的几种数据类型
2016/01/02 Python
Python程序中用csv模块来操作csv文件的基本使用教程
2016/03/03 Python
python连接mysql有哪些方法
2020/06/24 Python
Keras loss函数剖析
2020/07/06 Python
Python容器类型公共方法总结
2020/08/19 Python
html5 offlline 缓存使用示例
2013/06/24 HTML / CSS
Html5+CSS3+EL表达式问题小结
2020/12/19 HTML / CSS
英国汽车零件购物网站:GSF Car Parts
2019/05/23 全球购物
美国眼镜在线零售商:Dualens
2019/12/07 全球购物
解决python 输出到csv 出现多空行的情况
2021/03/24 Python
大学毕业寄语大全
2014/04/10 职场文书
学习型班组申报材料
2014/05/31 职场文书
人力资源管理专业求职信
2014/07/23 职场文书
呐喊读书笔记
2015/06/30 职场文书
使用Python开发冰球小游戏
2022/04/30 Python
HTML中实现音乐或视频自动播放案例详解
2022/05/30 HTML / CSS