老生常谈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实现Linux下守护进程的编写方法
Aug 22 Python
初学Python实用技巧两则
Aug 29 Python
在Python的Flask框架中验证注册用户的Email的方法
Sep 02 Python
web.py 十分钟创建简易博客实现代码
Apr 22 Python
对python-3-print重定向输出的几种方法总结
May 11 Python
python实现遍历文件夹修改文件后缀
Aug 28 Python
详解python uiautomator2 watcher的使用方法
Sep 09 Python
django 框架实现的用户注册、登录、退出功能示例
Nov 28 Python
Python3加密解密库Crypto的RSA加解密和签名/验签实现方法实例
Feb 11 Python
Pycharm+Python工程,引用子模块的实现
Mar 09 Python
python 逆向爬虫正确调用 JAR 加密逻辑
Jan 12 Python
Python如何让字典保持有序排列
Apr 29 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
destoon后台网站设置变成空白的解决方法
2014/06/21 PHP
php使用sql server验证连接数据库的方法
2014/12/25 PHP
PHP编程中的Session阻塞问题与解决方法分析
2017/08/07 PHP
php 下 html5 XHR2 + FormData + File API 上传文件操作实例分析
2020/02/28 PHP
JS中简单的实现像C#中using功能(有源码下载)
2007/01/09 Javascript
DOM相关内容速查手册
2007/02/07 Javascript
Js数组的操作push,pop,shift,unshift等方法详细介绍
2012/12/28 Javascript
改变文件域的样式实现思路同时兼容ie、firefox
2013/10/23 Javascript
Extjs4实现两个GridPanel之间数据拖拽功能具体方法
2013/11/21 Javascript
JQuery获取与设置HTML元素的内容或文本的实现代码
2014/06/20 Javascript
JavaScript模拟重力状态下抛物运动的方法
2015/03/03 Javascript
JS数组array元素的添加和删除方法代码实例
2015/06/01 Javascript
IE9+已经不对document.createElement向下兼容的解决方法
2015/09/14 Javascript
javascript跑马灯抽奖实例讲解
2020/04/17 Javascript
jQuery File Upload文件上传插件使用详解
2016/12/06 Javascript
Bootstrap缩略图的创建方法
2017/03/22 Javascript
JavaScript验证知识整理
2017/03/24 Javascript
jquery仿微信聊天界面
2017/05/06 jQuery
vue利用better-scroll实现轮播图与页面滚动详解
2017/10/20 Javascript
vue组件编写之todolist组件实例详解
2018/01/22 Javascript
Javascript Promise用法详解
2018/05/10 Javascript
JS实现textarea通过换行或者回车把多行数字分割成数组并且去掉数组中空的值
2018/10/29 Javascript
layer.open的自适应及居中及子页面标题的修改方法
2019/09/05 Javascript
原生js实现html手机端城市列表索引选择城市
2020/06/24 Javascript
Python 过滤字符串的技巧,map与itertools.imap
2008/09/06 Python
解决Python中由于logging模块误用导致的内存泄露
2015/04/23 Python
Python+matplotlib绘制不同大小和颜色散点图实例
2018/01/19 Python
Python中将dataframe转换为字典的实例
2018/04/13 Python
使用python搭建服务器并实现Android端与之通信的方法
2019/06/28 Python
自定义Django Form中choicefield下拉菜单选取数据库内容实例
2020/03/13 Python
python:HDF和CSV存储优劣对比分析
2020/06/08 Python
scrapy结合selenium解析动态页面的实现
2020/09/28 Python
中班开学寄语
2014/04/04 职场文书
个人维稳承诺书
2015/05/04 职场文书
网络舆情信息简报
2015/07/21 职场文书
MySQL一劳永逸永久支持输入中文的方法实例
2022/08/05 MySQL