Python 装饰器初探

在谈及Python的时候,装饰器一直就是道绕不过去的坎。面试的时候,也经常会被问及装饰器的相关知识。总感觉自己的理解很浅显,不够深刻。是时候做出改变,对Python的装饰器做个全面的了解了。

1. 函数装饰器

直接上代码,看看装饰器到底干了些什么?

from functools import wraps
import timedef time_cost(func):@wraps(func)def f(*args, **kwargs):start_time = time.time()func(*args, **kwargs)end_time = time.time()print(end_time - start_time)return f@time_cost
def test(*args, **kwargs):time.sleep(1.0)if __name__ == "__main__":test()

上面的Python代码,运行后,会给出test函数的执行时间。代码的执行顺序大概如下,首先是将test作为值传递给time_cost函数,返回函数f,然后再调用f,这是带有time_cost装饰器的test函数的大致执行过程。

从中,不难看出,即使不使用装饰器符号,我们利用Python的语言特性,也能达成上述目的。用装饰器符号的好处是简化了代码,增加了代码的可读性。

这是一段非常简单的对函数使用装饰器的Python代码。等等,@wraps(func)是什么鬼?悄悄干了什么哇?

我们稍微修改下上述代码,结果如下:

from functools import wraps
import timedef time_cost(func):def f(*args, **kwargs):start_time = time.time()func(*args, **kwargs)end_time = time.time()print(end_time - start_time)print('hello world')return f@time_cost
def test(*args, **kwargs):time.sleep(1.0)if __name__ == "__main__":print(test.__name__)

发现输出了hello world,同时输出test.__name__,居然变成了f,并不是我们预期的test。根据这样的输出结果,我们不难得出,其实被装饰器time_cost修饰过的函数test本质上已经等同于time_cost(test),此时访问test.__name__实际上访问的是time_cost(test).__name__,得到的当然就是f啦。当我们加上@wraps(func),此时test.__name__变成了test

下面介绍带参数的装饰器,更加难了。在谈论带参数的装饰器之间,首先得引入一个概念,那就”闭包“。如果你以前用过脚本语言,比如JavaScript,那么一定会很熟悉闭包这个概念。下面是一个闭包样例

def add(a):def wrapper(c):return a + creturn wrapperif __name__ == "__main__":add3 = add(3)add9 = add(9)print(add3(4) == 7)print(add9(1) == 10)

从中可以看出,在调用add3的时候,wrapper内部还可以访问到a的值,这就是闭包的作用。理解了闭包,理解带参数的装饰器就容易多了。

from functools import wrapsdef logging(level):def outer_wrapper(func):@wraps(func)def inner_wrapper(*args, **kwargs):print("[{level}]: enter function {func}()".format(level=level,func=func.__name__))return func(*args, **kwargs)return inner_wrapperreturn outer_wrapper@logging(level='WARN')
def show(msg):print('message:{}'.format(msg))if __name__ == "__main__":show('hello world!')

上面给出了一个带参数装饰器的示例。根据我们前面的铺垫,我们不难分析得出,上面的执行过程是logging(level='WARN')->outer_wrapper(show)->inner_wrapper(),所以我们可以理解,在被logging修饰后的show其实就是logging(level='WARN')(show),执行show('hello world!')其实就是在执行logging(level='WARN')(show)()。注意与不带参数的装饰器的区别,带参数的装饰器比不带参数的装饰器多套了一层,对应的装饰器也有了调用。因为在使用装饰器的时候,带了括号,所以装饰器本身多套了一层。被装饰器修饰过的函数在被调用的时候,实际上执行的是装饰器最内层的函数,其余层的在函数被修饰时就已经执行了。

是不是觉得非常自然?对的,我以前对装饰器的理解也就停留在不带参数的装饰器这一深度。

2. 基于类实现的装饰器

依然先上代码

from functools import wraps
import timeclass time_cost:def __init__(self, func):self.func = funcdef __call__(self, *args, **kwargs):start_time = time.time()result = self.func(*args, **kwargs)end_time = time.time()print(end_time - start_time)return result@time_cost
def test(*args, **kwargs):time.sleep(1.0)if __name__ == "__main__":test()

上面的基于类实现的不带参数的装饰器实际上利用的是Python中的可调用对象特性,凡是实现了__call__方法的类的实例是可以被调用的。因此被time_cost修饰过的test函数本质上已经变成了time_cost类的实例了。调用test方法的时候,实际上执行的是__call__方法。

下面介绍稍微复杂一点的基于类实现的带有参数的装饰器。

from functools import wrapsclass logging:def __init__(self, level):self.level = leveldef __call__(self, func):@wraps(func)def wrapper(*args, **kwargs):print("[{level}]: enter function {func}()".format(level=self.level,func=func.__name__))return func(*args, **kwargs)return wrapper@logging(level='WARN')
def show(msg):print('message:{}'.format(msg))if __name__ == "__main__":show('hello world!')

不同于基于类实现的不带参数的装饰器,基于类实现的带参数的装饰器在__call__里面多了一层wrapper。被装饰器修饰的show方法本质上是logging(level='WARN')(show),此时调用show方法,实际上执行的是wrapper方法。

现在看来,其实装饰器也没有很复杂,在实际的项目中用装饰器可以带来很大便利。

转载于:https://www.cnblogs.com/crackpotisback/p/10197698.html

Python 装饰器初探相关推荐

  1. Python装饰器详解,详细介绍它的应用场景

    装饰器的应用场景 附加功能 数据的清理或添加: 函数参数类型验证 @require_ints 类似请求前拦截 数据格式转换 将函数返回字典改为 JSON/YAML 类似响应后篡改 为函数提供额外的数据 ...

  2. python装饰器理解_如何理解Python装饰器?

    首先,本垃圾文档工程师又来了.开始日常的水文写作.起因是看到这个问题如何理解Python装饰器?,正好不久前给人讲过这些,本垃圾于是又开始新的一轮辣鸡文章写作行为了. 预备知识 首先要理解装饰器,首先 ...

  3. python简单装饰器_简单介绍Python装饰器(一)

    装饰器的作用 相信大家在 探索过程中已经了解装饰器的作用,也有很多花里胡哨的介绍. 这次小冰也来讲解一下关于Python装饰器的一些小知识. 它的作用: 性能测试 日志 安全验证 ...... 相信大 ...

  4. Python装饰器的神奇功能:自动打印每个方法耗时

    问题: 运行代码时,尤其对于大型项目需要分析每个环节方法耗时的.每个方法前后都写计算耗时及日志打印太繁琐了,而且代码不精简. 解决: Python装饰器类似于Spring的 AOP(Aspect Or ...

  5. python装饰器教学_Python装饰器学习(九步入门)

    这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方式. 第一步:最简单的函数,准备附加额外功能 # -*- coding:gbk -*- '''示例1: 最简单的函数,表示调用了两次 ...

  6. 简单介绍python装饰器

    这篇文章简单介绍一下python装饰器,希望对你们有所帮助. 简单正常python例子: def up(text):return text.upper() #转成大写 def lo(text):ret ...

  7. Python装饰器是什么?使用Python装饰器实现计算程序(函数)运行时间的功能

    Python装饰器是什么?使用Python装饰器实现计算程序(函数)运行时间的功能 目录

  8. python 装饰器示例

    python 装饰器示例 import timedef decorator(func): # 传函数def wrapper(*args, **kwargs): # 传参数(也可以传固定参数)start ...

  9. [转]python 装饰器

    以前你有没有这样一段经历:很久之前你写过一个函数,现在你突然有了个想法就是你想看看,以前那个函数在你数据集上的运行时间是多少,这时候你可以修改之前代码为它加上计时的功能,但是这样的话是不是还要大体读读 ...

最新文章

  1. bootstrap学习(二)页面
  2. 按钮自动居中布局_CSS布局技巧
  3. coap python3_node-coap入门(三)——Observe
  4. c语言学生成绩查询系统2018,南昊网上阅卷学生成绩查询系统
  5. 《炉石传说》建筑设计欣赏(7):采用Google.ProtocolBuffers处理网络消息
  6. java 反射 泛型 构造函数_Java复习——反射和泛型的复习
  7. 怎样彻底删除系统服务项
  8. ​网易首支 AI 生成歌曲《醒来》正式发布;FSF :苹果 OCSP 事故在道德上不可接受;CentOS 8.3 发布|极客头条...
  9. Redis数据库 | 快速入门 | 自学笔记
  10. IOS Apple Pay
  11. python 数学画图工具_[python][数据分析] matplotlib 和 pyecharts的绘图工具
  12. 微信安装包 11 年膨胀 575 倍?QQ安装包800M?谁在抢你的手机内存?
  13. 大数据分析平台建设项目需求报告与技术方案
  14. 51单片机红绿灯(十字路口智能控制系统)
  15. matlab三次根号怎么打,matlab 3次根号怎么写
  16. 图灵计算机模型意义,图灵机有什么意义_学习图灵机模型中遇到的问题 - 人工智能 - 电子发烧友网...
  17. 暴雪战网国际版[国区登录战网国际版方法]
  18. (翻译)简化模式(Reduce)
  19. iSlide(PPT插件) V3.4.5
  20. Android左右滑动控件实现开关的切换效果

热门文章

  1. 2018.2.2PHPstrom破解版
  2. IAR切BANK--BANK说明
  3. gulp+PC前端静态页面项目开发
  4. Linux C 数据结构——二叉树
  5. python文件头--文件编码指定
  6. 我的Mac os x中的nginx配置文件nginx.conf
  7. Windows 7 文件夹共享
  8. 如何在Microwindows中显示图片和安装字体
  9. [react] React15和16别支持IE几以上?
  10. React开发(123):ant design学习指南