一、什么是装饰器

装饰器顾名思义就是装饰的工具,指的是在不改变被装饰对象的源代码以及调用方式的前提下,为被装饰对象增加额外的功能,装饰器经常使用在以下几个场景,比如:插入日志、性能测试、事务处理、缓存、权限校验、测试等应用场景

二、为何要有装饰器

软件的设计应该遵循开放封闭原则,即对扩展是开放的,而对修改是封闭的。我们对现有的代码需要扩展功能时不能修改源代码以及调用方式,否则稍有不慎,就会导致整个程序报错,因为我们要修改的这段程序可能在很多地方调用,所以修改源代码和调用方式是很不明智的。而装饰器则可以在不修改源代码和调用方式的情况为原程序拓展新的功能。

三、装饰器的实现思路

现在有一段程序,用来计算0~n的和

def add(n):sum = 0for i in range(n):sum += ireturn sumres = add(100000)
print(res)输出:
>> 4999950000

如果想计算一下这个函数的运行时间,在修改源代码的情况下我们是这样做的

import timedef add(n):start = time.time()sum = 0for i in range(n):sum += istop = time.time()print('function run time: %s'%(stop - start))return sumres = add(100000)
print(res)输出:
>> function run time: 0.014251947402954102
>> 4999950000

上面的代码虽然实现了为函数体扩展计算函数运行时间的功能,但是违背了我们开头所说的开放封闭原则,为函数增加新功能是不能修改源代码的!那么在不修改源代码的情况下,如何为其加新功能呢?我们可以在调用的时候,计算函数体运行的时间

import timedef add(n):sum = 0for i in range(n):sum += ireturn sumstart = time.time()
res = add(100000)
print(res)
stop = time.time()
print('function run time: %s'%(stop - start))输出:
>> 4999950000
>> function run time: 0.014489889144897461

现在实现了在不修改源代码以及调用方式的情况下,为原函数增加了计算运行时间的功能,既没有违背开放封闭原则,也扩展了新功能,看似是完美,但是有没有想过,我们可能在很多地方都调用了add函数,那我们要在每次调用的时候都写这么一堆代码?这完全是重复且冗余的代码,说到重复冗余的代码,有些同学就想到了,我们可以封装成一个函数啊!啧啧~

import timedef add(n):sum = 0for i in range(n):sum += ireturn sumdef wrapper():start = time.time()res = add(100000)print(res)stop = time.time()print('function run time: %s'%(stop - start))res = wrapper()
print(res)输出:
>> 4999950000
>> function run time: 0.015117883682250977
>> None

好了,现在我们将拓展的功能封装成一个函数了,这样我们就省去了一大堆的重复冗余代码,但是大家有没有察觉到,上面的代码是有问题的

第一个问题: add 函数是有返回值的,但是我们的新函数wrapper(这里可以称之为装饰器了)返回值是None。

第二个问题 :调用 add 函数是需要传一个参数进去的,而装饰器函数却没有传参,这不就违背了文章开头提到的开放封闭原则了么

是的 所以我们还可以将函数优化一下
解决第一个问题:在装饰器函数里面要把调用add函数返回的值给return出来
解决第二个问题:在装饰器函数里面,把调用add函数时传的值参数化

import timedef add(n):sum = 0for i in range(n):sum += ireturn sumdef wrapper(n):start = time.time()res = add(n)stop = time.time()print('function run time: %s'%(stop - start))return resres = wrapper(10000)
print(res)输出:
>> function run time: 0.0009837150573730469
>> 49995000

至此,我们已经在不违背开放封闭原则的前提下为 add 函数拓展了计算运行时间的功能,这样就大功告成了么? 不不不,我们现在只是实现了为 add 函数扩展了计算运行时间的功能而已,如果我们还需要为其他函数增加计算运行时间的功能怎么办,难道要再写一个吗?装饰器是通用的,所以我们要考虑一下,函数名能不能参数化?(函数对象的概念), 我们先尝试一下把函数名参数化,看看会有什么问题~

import timedef add(n):sum = 0for i in range(n):sum += ireturn sumdef index(x,y):print('from index %s:%s'%(x,y))def wrapper(func,n):start = time.time()res = func(n)stop = time.time()print('function run time: %s'%(stop - start))return resres_add = wrapper(add,10000)
print(res_add)wrapper(index,1,2)输出:
>> function run time: 0.0007648468017578125
>> 49995000File "/Users/qts/PycharmProjects/QTS/test/ll.py", line 45, in <module>res_index = wrapper(index,1,2)
TypeError: wrapper() takes 2 positional arguments but 3 were given

将函数名参数化以后,新增index函数进行测试一下,调用wrapper为index函数增加扩展功能,出现了问题,什么问题呢?我们来看一下

第一个问题:index 函数是需要传两个参数的,但是装饰器函数wrapper 只有一个位置形参可以传值,另一个参数怎么传?

首先解决第一个问题,被装饰的对象有几个参数我们不知道,那么我们可不可以用不定长参数来接收呢,这里就用到args,**kwags来接收传进来的参数打包成一个元组或者字典,然后在函数体内使用args,**kwargs 把元组或者字典重新打散传值给func,这样就实现了我们给wrapper传什么参数,func接收的就是什么参数

import timedef add(n):sum = 0for i in range(n):sum += ireturn sumdef index(x,y):print('from index %s:%s'%(x,y))def wrapper(func,*args,**kwargs):start = time.time()res = func(*args,**kwargs)stop = time.time()print('function run time: %s'%(stop - start))return resres_add = wrapper(add,10000)
print(res_add)wrapper(index,1,2)输出:
>> function run time: 0.0012209415435791016
>> 49995000
>> from index 1:2
>> function run time: 1.0013580322265625e-05

第二个问题:我们在调用的时候多传了一个形参func,因为我们需要通过func 传入被装饰的函数对象,装饰器函数改动了形参,改变了原函数的调用方式,违背了开头说的开放封闭原则

解决第二个问题就使用到了闭包函数的概念,不了解的小伙伴可以去百度一下,闭包函数其实就是第二种为函数体传参的方式,通过闭包函数的方式我们解决了第二个问题,现在我们就已经实现了完整的无参装饰器了。

import timedef add(n):sum = 0for i in range(n):sum += ireturn sumdef index(x,y):print('from index %s:%s'%(x,y))def outter(func):def wrapper(*args,**kwargs):start = time.time()res = func(*args,**kwargs)stop = time.time()print('function run time: %s'%(stop - start))return resreturn wrapperadd = outter(add)
res = add(10000)
print(add)index = outter(index)
index(2,1)输出:
>> function run time: 0.0017659664154052734
>> 49995000
>> from index 2:1
>> function run time: 1.621246337890625e-05

另外提一点命名空间的概念,调用outter函数返回的是wrapper的函数对象,我们在全局作用域拿到后可以赋值给x,可以赋值给y,也可以赋值给原函数名,把原函数名指向函数对象给覆盖,我们就像是在调用原函数名一样调用装饰器了,是不是很完美~,其实python就已经帮我们实现这件事了,这里要介绍一个令人快乐的东西叫语法糖

语法糖
在被装饰对象上面加上@装饰器,就相当于 add = outter(add) 然后我们调用add时,其实就是调用的outter函数返回的wrapper

import timedef outter(func):def wrapper(*args,**kwargs):start = time.time()res = func(*args,**kwargs)stop = time.time()print('function run time: %s'%(stop - start))return resreturn wrapper@outter
def add(n):sum = 0for i in range(n):sum += ireturn sum@outter
def index(x,y):print('from index %s:%s'%(x,y))res = add(10000)
print(res)index(1,2)输出:
>> function run time: 0.001477956771850586
>> 49995000
>> from index 1:2
>> function run time: 3.314018249511719e-05

再多说一句哈,现在我们实现的装饰器是不是天衣无缝了?我们在调用的时候完全不用关心原函数扩展了啥功能,而且也不用改变原来的调用方式,但是还是有一点区别的,比如函数自带的一些方法 namedoc 还是有区别的,我们既然要伪装了,就一定要力争完美,这里可以使用 functools 的 wraps 方法

import time
from functools import wrapsdef outter(func):@wraps(func)def wrapper(*args,**kwargs):start = time.time()res = func(*args,**kwargs)stop = time.time()print('function run time: %s'%(stop - start))return resreturn wrapper@outter
def add(n):sum = 0for i in range(n):sum += ireturn sum@outter
def index(x,y):print('from index %s:%s'%(x,y))

无参装饰器讲完了,后续有机会讲一下有参装饰器以及叠加装饰器的用法~

python函数装饰器一篇入魂相关推荐

  1. python函数装饰器详解_Python语言函数装饰器用法实例详解

    这篇文章主要介绍了Python语言函数装饰器用法,以实例形式较为详细的分析了Python函数装饰器的常见使用技巧,需要的朋友可以参考下,希望对大家学习Python语言有所帮助. 本文实例讲述了pyth ...

  2. python装饰器函数-Python函数装饰器常见使用方法实例详解

    本文实例讲述了Python函数装饰器常见使用方法.分享给大家供大家参考,具体如下: 一.装饰器 首先,我们要了解到什么是开放封闭式原则? 软件一旦上线后,对修改源代码是封闭的,对功能的扩张是开放的,所 ...

  3. python装饰器-Python @函数装饰器及用法(超级详细)

    前面章节中,我们已经讲解了 Python 内置的 3 种函数装饰器,分别是 @staticmethod.@classmethod 和 @property,其中 staticmethod().class ...

  4. python装饰器原理-Python函数装饰器原理与用法详解

    本文实例讲述了Python函数装饰器原理与用法.分享给大家供大家参考,具体如下: 装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器的返回值 ...

  5. python函数-装饰器

    python函数-装饰器 1.装饰器的原则--开放封闭原则 开放:对于添加新功能是开放的 封闭:对于修改原功能是封闭的 2.装饰器的作用 在不更改原函数调用方式的前提下对原函数添加新功能 3.装饰器的 ...

  6. 关于python中def的高级用法,def中套def,python函数装饰器

    Python函数装饰器 装饰器(Decorators)是 Python 的一个重要部分.简单地说:他们是修改其他函数的功能的函数.他们有助于让我们的代码更简短,也更Pythonic(Python范儿) ...

  7. python装饰器函数-Python函数装饰器指南

    Python 具有强大的功能和富有表现力的语法.我最喜欢的装饰之一.在设计模式的上下文中,装饰器动态更改方法或类功能,而不必直接使用子类.当您需要扩展功能,但不想修改原函数时,这是理想的选择.我们可以 ...

  8. python装饰器实例-Python函数装饰器--实例讲解

    一.装饰器定义: 1.装饰器的本质为函数: 2.装饰器是用来完成被修饰函数的附加功能的 所以:装饰器是用来完成被修饰函数附属功能的函数 装饰器的要求: 1.不能修改被修饰函数的源代码: 2.不能更改被 ...

  9. python装饰器函数-python函数装饰器

    什么是装饰器 装饰器是一个可调用的对象,其参数是另一个函数(被装饰的函数).装饰器可能会: 1,处理被装饰的函数,然后把它返回 2,将其替换成另一个函数或者对象 若有个名为decorate的装饰器,则 ...

最新文章

  1. hibernate关联关系(一对多)
  2. UVa 11121 Base -2(负数进制)
  3. 网络编程知识预备(1) ——了解OSI网络模型
  4. 人生没有对与错,只是选择不同
  5. 按一个按钮会随机死人_《饥荒》那些年坑爹的随机地图,最后一个简直笑死人...
  6. 与虚拟现实技术相关联的计算机技术,虚拟现实技术与其他技术的关系是什么?-VR-形象思维VR...
  7. 北航计算机2014复试上机题,北航计算机系考研复试上机真题及答
  8. Ubuntu 14.04 安装 WPS
  9. 勇探计算机城堡教学反思,神秘的城堡教学反思
  10. python 字典组成的列表 差集_python 中 如何 获取两个 字典组成的列表的差集?
  11. matlab晶闸管整流电路,三相桥式全控整流电路 MATLAB/SIMULINK电力电子电路仿真
  12. js 对中文字符的 解码 与 编码
  13. 五家共井 穷举法_测井曲线代码一览表
  14. 教你如何关闭445端口
  15. 统计红楼梦出场次数最多的十个人物
  16. ESN学习笔记——echotorch(1)介绍
  17. 什么样的电子签名有法律效力
  18. android 查看路由器ip,如何查看路由器ip地址进入登录页面
  19. 中国传媒大学计算机课程表,中国传媒大学播本课表.pdf
  20. web服务器性能测试---服务器性能测试实例

热门文章

  1. java bitwise_Java Core.bitwise_and方法代码示例
  2. 图标字体的优缺点和使用
  3. 2020年国内优秀原创IT技术书都在这了
  4. 牛逼!这届WWDC依旧展现了那个让你无法复制的苹果!
  5. 【Android容器组件—LinearLayout】
  6. Verilog语言初学1
  7. 398高校毕业设计选题
  8. 设计模式笔记——代理模式
  9. 专访 | 宋星 10 年启示录:数字营销如何觐见下一个 10 年?
  10. 9.Django应用及分布式路由