浅析装饰器

通常情况下,给一个对象添加新功能有三种方式:

  • 直接给对象所属的类添加方法;
  • 使用组合;(在新类中创建原有类的对象,重复利用已有类的功能)
  • 使用继承;(可以使用现有类的,无需重复编写原有类进行功能上的扩展)

一般情况下,优先使用组合,而不是继承。但是装饰器属于第四种,动态的改变对象从而扩展对象的功能。

一般装饰器的应用场景有打印日志,性能测试,事务处理,权限校验;

Python 内置装饰器的工作原理

理解Python装饰器工作原理,首先需要理解闭包这一概念。闭包指的是一个函数嵌套一个函数,内部嵌套的函数调用外部函数的

变量,外部函数返回内嵌函数,这样的结构就是闭包。

装饰器就是闭包的一种应用,但是装饰器参数传递的是函数。

简单的闭包示例:

def add_num(x):
def sum_num(y):
return x+y
return sum_num add_num5 = add_num(5)
total_num = add_num5(100)
print(total_num)

注解:

  • add_num外函数接受参数x,返回内函数sum_num,而内函数sum_num接受参数y,将和add_num外函数接受参数x相加,返回结果。add_num5对象则是定义了add_num外函数接受的参数x为5,最后add_num5(100)返回的结果则是105。

装饰器的基本使用

简单计算函数运行时间装饰器示例:

def times_use(func):
def count_times(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(end-start)
return result
return count_times @times_use
def test_decorator():
time.sleep(2)
print("Test Decorator")
test_decorator()

注解:

  • 这里将函数test_decorator作为参数,传入到times_use函数中,然后内部函数count_times则是会保留原有test_decorator函数代码逻辑,在执行test_decorator前保存执行前时间,然后和执行后的时间进行比较,得出相应的耗时。
  • 通过装饰器的好处则是能在保留原有函数的基础上,不用进行对原有函数的修改或者增加新的封装,就能修饰函数增加新的功能。

根据日志等级打印日志装饰器示例(带参数的装饰器):

def use_logging(level):
def decorator(func):
def wrapper(*args, **kwargs):
if level == "warn":
logging.warn("%s is running"% func.__name__)
result = func(*args, **kwargs)
print(result)
return result
return wrapper
return decorator @use_logging("warn")
def test_decorator():
print("Test Decorator")
return "Success"
test_decorator()

计算函数运行时间的类装饰器示例:

class logTime:
def __init__(self, use_log=False):
self._use_log = use_log def __call__(self, func): def _log(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
print(result)
end_time = time.time()
if self._use_log:
print(end_time-start_time)
return result
return _log @logTime(True)
def test_decorator():
time.sleep(2)
print("Test Decorator")
return "Success"

functools wraps使用场景

使用装饰器虽然能在保存原有代码逻辑的基础上扩展功能,但是原有函数中的元信息会丢失,比如__name__, __doc__,参数列表。针对这种情况

可以使用functools.wraps,wraps也是一个装饰器,但是会将原函数的元信息拷贝到装饰器函数中。

具体使用方法:

from functools import wraps

def use_logging(level):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs): if level == "warn":
logging.warn("%s is running"% func.__name__)
result = func(*args, **kwargs)
print(result)
return result
return wrapper
return decorator @use_logging("warn")
def test_decorator():
"""" Test Decorator DocString""""
time.sleep(2)
print("Test Decorator")
return "Success" print(test_decorator.__name__)
print(test_decorator.__doc__)

注解:

  • wraps装饰器将传入的test_decorator函数中的元信息拷贝到wrapper这个装饰器函数中,使得wrapper拥有和test_decorator的

    元信息。

关于装饰器的执行顺序

在日常业务中经常会使用多个装饰器,比如权限验证,登录验证,日志记录,性能检测等等使用场景。所以在使用多个装饰器

时,就会涉及到装饰器执行顺序的问题。先说结论,关于装饰器执行顺序,可以分为两个阶段:(被装饰函数)定义阶段、(被装饰函数)执行阶段。

  • 函数定义阶段,执行顺序时从最靠近函数的装饰器开始,从内向外的执行;
  • 函数执行阶段,执行顺序时从外而内,一层层的执行;

多装饰器示例:

def decorator_a(func):
print("Get in Decorator_a") def inner_a(*args, **kwargs):
print("Get in Inner_a")
result = func(*args, **kwargs)
return result
return inner_a def decorator_b(func):
print("Get in Decorator_b") def inner_b(*args, **kwargs):
print("Get in Inner_b")
result = func(*args, **kwargs)
return result
return inner_b @decorator_b
@decorator_a
def test_decorator():
"""test decorator DocString"""
print("Test Decorator")
return "Success"

运行结果:

Get in Decorator_a
Get in Decorator_b
Get in Inner_b
Get in Inner_a
Test Decorator

代码注解:

  • 上述函数使用装饰器可以相当于decorator_b(decorator_a(test_decorator()),即test_dcorator函数作为参数传入到decorator_a函数中,然后打印"Get in Decorator_a",并且返回inner_a函数,给上层decorator_b函数,decorator_b函数接受了作为参数的inner_a函数,打印"Get in Decorator_b",然后返回inner_b函数;
  • 此时test_decorator(),即调用了该inner_b函数,inner_b函数打印"Get in inner_b",然后调用inner_a函数,inner_a打印了"Get in Decorator_a",最后调用test_decorator函数。这样从最外层看,就像直接调用了test_decorator函数一样,但是可以在刚刚的过程中实现功能的扩展;

参考链接

https://www.zhihu.com/question/26930016

https://segmentfault.com/a/1190000007837364

https://blog.csdn.net/u013411246/article/details/80571462

粗浅聊聊Python装饰器的更多相关文章

  1. 关于python装饰器

    关于python装饰器,不是系统的介绍,只是说一下某些问题 1 首先了解变量作用于非常重要 2 其次要了解闭包 def logger(func): def inner(*args, **kwargs) ...

  2. python装饰器通俗易懂的解释!

    1.python装饰器 刚刚接触python的装饰器,简直懵逼了,直接不懂什么意思啊有木有,自己都忘了走了多少遍Debug,查了多少遍资料,猜有点点开始明白了.总结了一下解释得比较好的,通俗易懂的来说 ...

  3. Python 装饰器学习

    Python装饰器学习(九步入门)   这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方式. 第一步:最简单的函数,准备附加额外功能 1 2 3 4 5 6 7 8 # -*- c ...

  4. python 装饰器修改调整函数参数

    简单记录一下利用python装饰器来调整函数的方法.现在有个需求:参数line范围为1-16,要求把9-16的范围转化为1-8,即9对应1,10对应2,...,16对应8. 下面是例子: def fo ...

  5. python 装饰器学习(decorator)

    最近看到有个装饰器的例子,没看懂, #!/usr/bin/python class decorator(object): def __init__(self,f): print "initi ...

  6. Python装饰器详解

    python中的装饰器是一个用得非常多的东西,我们可以把一些特定的方法.通用的方法写成一个个装饰器,这就为调用这些方法提供一个非常大的便利,如此提高我们代码的可读性以及简洁性,以及可扩展性. 在学习p ...

  7. 关于python装饰器(Decorators)最底层理解的一句话

    一个decorator只是一个带有一个函数作为参数并返回一个替换函数的闭包. http://www.xxx.com/html/2016/pythonhexinbiancheng_0718/1044.h ...

  8. Python装饰器由浅入深

    装饰器的功能在很多语言中都有,名字也不尽相同,其实它体现的是一种设计模式,强调的是开放封闭原则,更多的用于后期功能升级而不是编写新的代码.装饰器不光能装饰函数,也能装饰其他的对象,比如类,但通常,我们 ...

  9. Python装饰器与面向切面编程

    今天来讨论一下装饰器.装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志.性能测试.事务处理等.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数 ...

随机推荐

  1. SpringBoot 配置文件以及依赖 加上跨域配置文件

    配置目录: application.properties的配置 #设置服务端口号 server.port = 8090 #配置数据源 spring.datasource.driver-class-na ...

  2. gojs插件使用教程

    目录 一.简介 二.简单使用 三.重要概念 1.TextBlock创建文本 2.Shape图形 3.Node节点(文本与图形结合) 4.Link箭头 四.数据绑定(前后端交互数据渲染) 五.去除水印 ...

  3. SpringBoot(八):SpringBoot中配置字符编码 Springboot中文乱码处理

    SpringBoot中配置字符编码一共有两种方式 方式一: 使用传统的Spring提供的字符编码过滤器(和第二种比较,此方式复杂,由于时间原因这里先不介绍了,后续补上) 方式二(推荐使用) 在appl ...

  4. 后端程序员之路 45、nginx CORS 跨域

    在提供api给其它应用使用时,有时我们会要限制它的跨域使用,而有时,我们又要用CORS来打破AJAX只能同源使用的限制 跨域资源共享 CORS 详解 - 阮一峰的网络日志http://www.ruan ...

  5. 【Azure 云服务】Azure Cloud Service在发布新部署后遇见不能RDP(远程连接)到实例时如何处理?

    Azure 云服务是PaaS 的一个示例. 与 Azure 应用服务一样,此技术设计用于支持可缩放.可靠且运营成本低廉的应用程序. 同样,应用服务托管在虚拟机 (VM) 上,Azure 云服务也是如此 ...

  6. Apache配置 10. 访问控制-禁止解析PHP

    (1)简述 对于使用PHP语言编写的网站,有一些目录是有需求上传文件的.如果网站代码有漏洞,让黑客上传了一个用PHP写的木马,由于网站可以执行PHP程序,最终会让黑客拿到服务器权限. 为了避免这种情况 ...

  7. P2766 最长不下降子序列问题 题解(网络流)

    题目链接 最长不下降子序列问题 解题思路 分成三小问解决. 第一小问,求\(LIS\),因为\(n<=500\),直接\(O(N^2)\)暴力求解即可. 第二三小问,建立模型用网络流求解. 对于 ...

  8. io流(File类)

    File类 创建一个file类(没有无参构造)的对象,并与文件进行关联 用File类来操作文件,代码如下: package com.bjsxt.test01; import java.io.File; ...

  9. P1328_生活大爆炸版石头剪刀布(JAVA语言)

    题目描述 石头剪刀布是常见的猜拳游戏:石头胜剪刀,剪刀胜布,布胜石头.如果两个人出拳一 样,则不分胜负.在<生活大爆炸>第二季第8集中出现了一种石头剪刀布的升级版游戏. 升级版游戏在传统的 ...

  10. 攻防世界 reverse EASYHOOK

    EASYHOOK XCTF 4th-WHCTF-2017 1 data=[ 0x61, 0x6A, 0x79, 0x67, 0x6B, 0x46, 0x6D, 0x2E, 0x7F, 0x5F, 2 ...