老生常谈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模拟登录百度代码分享(获取百度贴吧等级)
Dec 27 Python
Python实现的数据结构与算法之队列详解
Apr 22 Python
浅谈Python2获取中文文件名的编码问题
Jan 09 Python
Tensorflow中使用tfrecord方式读取数据的方法
Jun 19 Python
Python计算开方、立方、圆周率,精确到小数点后任意位的方法
Jul 17 Python
python实现淘宝秒杀脚本
Jun 23 Python
python安装numpy和pandas的方法步骤
May 27 Python
Python爬虫实现的根据分类爬取豆瓣电影信息功能示例
Sep 15 Python
创建Shapefile文件并写入数据的例子
Nov 26 Python
Python爬虫requests库多种用法实例
May 28 Python
Python try except else使用详解
Jan 12 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执行速度全攻略(上)
2006/10/09 PHP
PHP的开合式多级菜单程序
2006/10/09 PHP
[原创]php实现子字符串位置相互对调互换的方法
2016/06/02 PHP
如何打开php的gd2库
2017/02/09 PHP
php中实现字符串翻转的方法
2017/02/22 PHP
详解php curl带有csrf-token验证模拟提交方法
2018/04/18 PHP
使用PHPWord生成word文档的方法详解
2019/06/06 PHP
随机显示经典句子或诗歌的javascript脚本
2007/08/04 Javascript
javascript 打开页面window.location和window.open的区别
2010/03/17 Javascript
20款非常优秀的 jQuery 工具提示插件 推荐
2012/07/15 Javascript
UpdatePanel和Jquery冲突的解决方法
2013/04/01 Javascript
JS Replace 全部替换字符的用法小结
2013/12/24 Javascript
用js提交表单解决一个页面有多个提交按钮的问题
2014/09/01 Javascript
javascript html实现网页版日历代码
2016/03/08 Javascript
有关easyui-layout中的收缩层无法显示标题的解决办法
2016/05/10 Javascript
jQuery密码强度验证控件使用详解
2017/01/05 Javascript
js控制按钮,防止频繁点击响应的实例
2017/02/15 Javascript
Angular 4环境准备与Angular cli创建项目详解
2017/05/27 Javascript
express 项目分层实践详解
2018/12/10 Javascript
JS实现简单贪吃蛇小游戏
2020/10/28 Javascript
python二进制读写及特殊码同步实现详解
2019/10/11 Python
python pyenv多版本管理工具的使用
2019/12/23 Python
Python 日期的转换及计算的具体使用详解
2020/01/16 Python
pytorch加载自己的图像数据集实例
2020/07/07 Python
python绘图pyecharts+pandas的使用详解
2020/12/13 Python
5分钟弄清楚html5的drag and drop(小结)
2019/04/10 HTML / CSS
纽约21世纪百货官网:Century 21
2016/08/27 全球购物
中国电子产品外贸网站:MiniIntheBox
2017/02/06 全球购物
香奈儿美国官网:CHANEL美国
2020/05/20 全球购物
法学函授自我鉴定
2014/02/06 职场文书
在职证明书范本(2014新版)
2014/09/25 职场文书
小学教师暑期培训心得体会
2016/01/09 职场文书
Nginx工作原理和优化总结。
2021/04/02 Servers
Pandas自定义选项option设置
2021/07/25 Python
zabbix如何添加监控主机和自定义监控项
2022/08/14 Servers
MySQL远程无法连接的一些常见原因总结
2022/09/23 MySQL