老生常谈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之有容乃大的list(3)
Sep 15 Python
剖析Python的Twisted框架的核心特性
May 25 Python
Python实现视频下载功能
Mar 14 Python
Python 基础教程之str和repr的详解
Aug 20 Python
Python实现打印螺旋矩阵功能的方法
Nov 21 Python
python在每个字符后添加空格的实例
May 07 Python
python实现本地图片转存并重命名的示例代码
Oct 27 Python
对python多线程中互斥锁Threading.Lock的简单应用详解
Jan 11 Python
Python从入门到精通之环境搭建教程图解
Sep 26 Python
python如何实现word批量转HTML
Sep 30 Python
pytest配置文件pytest.ini的详细使用
Apr 17 Python
pytest实现多进程与多线程运行超好用的插件
Jul 15 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
discuz安全提问算法
2007/06/06 PHP
PHP 危险函数解释 分析
2009/04/22 PHP
php获取操作系统语言代码
2013/11/04 PHP
ThinkPHP模板中数组循环实例
2014/10/30 PHP
PHP输出日历表代码实例
2015/03/27 PHP
如何修改yii2.0自带的user表为其它的表
2017/08/01 PHP
Laravel Intervention/image图片处理扩展包的安装、使用与可能遇到的坑详解
2017/11/14 PHP
JavaScript 获取当前时间戳的代码
2010/08/05 Javascript
Jquery replace 字符替换实现代码
2010/12/02 Javascript
JS显示表格内指定行html代码的方法
2015/03/31 Javascript
jQuery入门基础知识学习指南
2015/08/14 Javascript
JS实现兼容性好,自动置顶的淘宝悬浮工具栏效果
2015/09/18 Javascript
又一枚精彩的弹幕效果jQuery实现
2016/07/25 Javascript
jQuery弹出层插件popShow(改进版)用法示例
2017/01/23 Javascript
使用AngularJS对表单提交内容进行验证的操作方法
2017/07/12 Javascript
详解win7 cmd执行vue不是内部命令的解决方法
2017/07/27 Javascript
Vue.js实现实例搜索应用功能详细代码
2017/08/24 Javascript
关于vue-router的那些事儿
2018/05/23 Javascript
vue实现未登录跳转到登录页面的方法
2018/07/17 Javascript
elementUI Vue 单个按钮显示和隐藏的变换功能(两种方法)
2018/09/04 Javascript
详解vue-router数据加载与缓存使用总结
2018/10/29 Javascript
Vue加载json文件的方法简单示例
2019/01/28 Javascript
微信小程序如何使用globalData的方法
2019/06/06 Javascript
浅谈Vue.set实际上是什么
2019/10/17 Javascript
[05:23]DOTA2-DPC中国联赛2月1日Recap集锦
2021/03/11 DOTA
python发送邮件功能实现代码
2016/07/15 Python
Python中对象迭代与反迭代的技巧总结
2016/09/17 Python
解决python3中自定义wsgi函数,make_server函数报错的问题
2017/11/21 Python
在python中实现强制关闭线程的示例
2019/01/22 Python
关于Python字符编码与二进制不得不说的一些事
2020/10/04 Python
HTML5 自动聚焦(autofocus)属性使用介绍
2013/08/07 HTML / CSS
人力资源经理自我评价
2014/01/04 职场文书
党员群众路线自我剖析材料
2014/10/06 职场文书
导游词之淮安明祖陵
2019/11/25 职场文书
Centos系统通过Docker安装并搭建MongoDB数据库
2022/04/12 MongoDB
前端框架ECharts dataset对数据可视化的高级管理
2022/12/24 Javascript