Python 装饰器学习以及实际使用场景实践

简介:

前言

前几天在看Flask框架,,不是非常的理解,回来补装饰器的功课。阅读很多的关于装饰器的文章,自己整理一下,适合自己的思路的方法和例子,与大家分享。

Python 装饰器学习以及实际使用场景实践


  
  
  1. app = Flask(__name__) 
  2. @app.route("/"
  3. def hello(): 
  4. return "Hello World!" 

1、装饰器是什么

装饰器是Python语言中的高级语法。主要的功能是对一个函数、方法、或者类进行加工,作用是为已经存在的对象添加额外的功能,提升代码的可读性。
装饰器是设计模式的一种,被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等

2、装饰器的语法

装饰器的语法如下:

当前Python的装饰器语法如下:


  
  
  1. @dec2 
  2. @dec1 
  3. def func(arg1, arg2, ...): 
  4.     .... 
  5.     return funx 
  6.  
  7. 上面的代码相当于: 
  8.  
  9. def func(arg1, arg2, ...): 
  10.     pass 
  11. func = dec2(dec1(func)) 

装饰器可以用def的形式来定义。装饰器接收一个可调用对象作为输入参数,并返回一个新的可调用对象。
装饰器新建了一个可调用对象,也就是return 返回的函数funx,在新增的函数中,可以添加我们需要的功能,并通过调用原有函数来实现原有函数的功能。

3、装饰器的使用

3.1不带参数的装饰器

定义装饰器非常的简单:


  
  
  1. def deco(func): 
  2.     """无参数调用decorator声明时必须有一个参数,这个参数将接收要装饰的方法""" 
  3.     print "before myfunc() called." 
  4.     func() 
  5.     print "after myfunc() called." 
  6.     return func 
  7.  
  8. @deco 
  9. def myfunc(): 
  10.     print " myfunc() called." 
  11.  
  12.  
  13. myfunc() 
  14. myfunc() 

定义好装饰器后,,,即可使用。上面这个装饰器在使用的时候有一个问题,即只在第一次被调用,并且原来的函数多执行一次。执行输出如下:


  
  
  1. before myfunc() called.   
  2. myfunc() called. 
  3. after myfunc() called. 
  4.  myfunc() called.   --函数多执行一次的输出 
  5.  myfunc() called.   --第二次调用,装饰器不生效 

要保证新函数每次被调用,使用下面的方法来定义装饰器


  
  
  1. def deco(func): 
  2.     """无参数调用decorator声明时必须有一个参数,这个参数将接收要装饰的方法""" 
  3.     def _deco(): 
  4.         print "before myfunc() called." 
  5.         func() 
  6.         print "after myfunc() called." 
  7.         #return func 不需要返回func 
  8.     retrun _deco 
  9. @deco 
  10. def myfunc(): 
  11.     print " myfunc() called." 
  12.     return 'OK' 
  13. myfunc() 
  14. myfunc() 

函数输出如下:


  
  
  1. before myfunc() called. 
  2.  myfunc() called. 
  3.   after myfunc() called. 
  4. before myfunc() called. 
  5.  myfunc() called. 
  6.   after myfunc() called. 

这样可以看到,装饰器每次都得到了调用。

3.2带参数的函数进行装饰器


  
  
  1. def deco(func): 
  2.     def _deco(a, b): 
  3.         print("before myfunc() called."
  4.         ret = func(a, b) 
  5.         print("  after myfunc() called. result: %s" % ret) 
  6.     return ret 
  7. return _deco 
  8.  
  9. @deco 
  10. def myfunc(a, b): 
  11.     print(" myfunc(%s,%s) called." % (a, b)) 
  12.     return a + b 
  13.   
  14. myfunc(12
  15. myfunc(34

输出:


  
  
  1. before myfunc() called. 
  2. myfunc() called. 
  3. After myfunc() called. result: 3 
  4. before myfunc() called. myfunc() called. After myfunc() called. result: 7 

内嵌函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数。

3.3装饰器带参数


  
  
  1. def decoWithArgs(arg): 
  2. """由于有参数的decorator函数在调用时只会使用应用时的参数而不接收被装饰的函数做为参数, 
  3.    所以必须返回一个decorator函数, 由它对被装饰的函数进行封装处理""
  4. def newDeco(func):    #定义一个新的decorator函数 
  5.     def replaceFunc():    #在decorator函数里面再定义一个内嵌函数,由它封装具体的操作 
  6.         print "Enter decorator %s" %arg    #进行额外操作 
  7.         return func()    #对被装饰函数进行调用 
  8.     return replaceFunc 
  9. return newDeco    #返回一个新的decorator函数 
  10.  
  11. @decoWithArgs("demo"
  12. def MyFunc():    #应用@decoWithArgs修饰的方法 
  13.     print "Enter MyFunc" 
  14.  
  15. MyFunc()    #调用被装饰的函数 

输出:
nter decorator demo
Enter MyFunc

这个情形适用于原来的函数没有参数,新增加打印的情况。常见适用的地方是增加函数的打印日志。

3.4对参数数量不确定的函数进行装饰

下面的例子是一个邮件异步发送的例子,函数的参数数据部确定,装饰器实现了对于邮件发送函数的异步发送。


  
  
  1. from threading import Thread 
  2.  
  3. def async(f): 
  4.     def wrapper(*args, **kwargs): 
  5.         thr = Thread(target = f, args = args, kwargs = kwargs) 
  6.         thr.start() 
  7.     return wrapper 
  8.  
  9. @async 
  10. def send_async_email(msg): 
  11.     mail.send(msg) 
  12.  
  13. def send_email(subject, sender, recipients, text_body, html_body): 
  14.     msg = Message(subject, sender = sender, recipients = recipients) 
  15.     msg.body = text_body 
  16.     msg.html = html_body 
  17.     send_async_email(msg) 

并且这个装饰器可以适用一切需要异步处理的功能,做到非常好的代码复用。

3.5让装饰器带类参数


  
  
  1. class locker: 
  2.     def __init__(self): 
  3.         print("locker.__init__() should be not called."
  4.           
  5.     @staticmethod 
  6.     def acquire(): 
  7.         print("locker.acquire() called.(这是静态方法)"
  8.           
  9.     @staticmethod 
  10.     def release(): 
  11.         print("  locker.release() called.(不需要对象实例)"
  12.   
  13. def deco(cls): 
  14.     '''cls 必须实现acquire和release静态方法''' 
  15.     def _deco(func): 
  16.         def __deco(): 
  17.             print("before %s called [%s]." % (func.__name__, cls)) 
  18.             cls.acquire() 
  19.             try
  20.                 return func() 
  21.             finally
  22.                 cls.release() 
  23.         return __deco 
  24.     return _deco 
  25.   
  26. @deco(locker) 
  27. def myfunc(): 
  28.     print(" myfunc() called."
  29.   
  30. myfunc() 
  31. myfunc() 

输出为:


  
  
  1. before myfunc called [__main__.locker]. 
  2. locker.acquire() called.(this is staticmethon) 
  3. myfunc() called. 
  4.   locker.release() called.(do't need object ) 
  5.  
  6. before myfunc called [__main__.locker]. 
  7. locker.acquire() called.(this is staticmethon) 
  8. myfunc() called. 
  9.   locker.release() called.(do't need object ) 

装饰器总结


当我们对某个方法应用了装饰方法后, 其实就改变了被装饰函数名称所引用的函数代码块入口点,使其重新指向了由装饰方法所返回的函数入口点。由此我们可以用decorator改变某个原有函数的功能,添加各种操作,或者完全改变原有实现。


作者:Stitch

来源:51CTO


相关文章
|
9月前
|
存储 JavaScript Java
(Python基础)新时代语言!一起学习Python吧!(四):dict字典和set类型;切片类型、列表生成式;map和reduce迭代器;filter过滤函数、sorted排序函数;lambda函数
dict字典 Python内置了字典:dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度。 我们可以通过声明JS对象一样的方式声明dict
502 2
|
9月前
|
存储 Java 数据处理
(numpy)Python做数据处理必备框架!(一):认识numpy;从概念层面开始学习ndarray数组:形状、数组转置、数值范围、矩阵...
Numpy是什么? numpy是Python中科学计算的基础包。 它是一个Python库,提供多维数组对象、各种派生对象(例如掩码数组和矩阵)以及用于对数组进行快速操作的各种方法,包括数学、逻辑、形状操作、排序、选择、I/0 、离散傅里叶变换、基本线性代数、基本统计运算、随机模拟等等。 Numpy能做什么? numpy的部分功能如下: ndarray,一个具有矢量算术运算和复杂广播能力的快速且节省空间的多维数组 用于对整组数据进行快速运算的标准数学函数(无需编写循环)。 用于读写磁盘数据的工具以及用于操作内存映射文件的工具。 线性代数、随机数生成以及傅里叶变换功能。 用于集成由C、C++
721 1
|
9月前
|
算法 Java Docker
(Python基础)新时代语言!一起学习Python吧!(三):IF条件判断和match匹配;Python中的循环:for...in、while循环;循环操作关键字;Python函数使用方法
IF 条件判断 使用if语句,对条件进行判断 true则执行代码块缩进语句 false则不执行代码块缩进语句,如果有else 或 elif 则进入相应的规则中执行
1622 1
|
9月前
|
测试技术 Python
Python装饰器:为你的代码施展“魔法”
Python装饰器:为你的代码施展“魔法”
397 100
|
10月前
|
设计模式 缓存 监控
Python装饰器:优雅增强函数功能
Python装饰器:优雅增强函数功能
394 101
|
9月前
|
缓存 Python
Python装饰器:为你的代码施展“魔法
Python装饰器:为你的代码施展“魔法
528 88
|
9月前
|
存储 数据采集 监控
Python定时爬取新闻网站头条:从零到一的自动化实践
在信息爆炸时代,本文教你用Python定时爬取腾讯新闻头条,实现自动化监控。涵盖请求、解析、存储、去重、代理及异常通知,助你构建高效新闻采集系统,适用于金融、电商、媒体等场景。(238字)
1537 2
|
9月前
|
存储 Java 索引
(Python基础)新时代语言!一起学习Python吧!(二):字符编码由来;Python字符串、字符串格式化;list集合和tuple元组区别
字符编码 我们要清楚,计算机最开始的表达都是由二进制而来 我们要想通过二进制来表示我们熟知的字符看看以下的变化 例如: 1 的二进制编码为 0000 0001 我们通过A这个字符,让其在计算机内部存储(现如今,A 字符在地址通常表示为65) 现在拿A举例: 在计算机内部 A字符,它本身表示为 65这个数,在计算机底层会转为二进制码 也意味着A字符在底层表示为 1000001 通过这样的字符表示进行转换,逐步发展为拥有127个字符的编码存储到计算机中,这个编码表也被称为ASCII编码。 但随时代变迁,ASCII编码逐渐暴露短板,全球有上百种语言,光是ASCII编码并不能够满足需求
383 4
|
10月前
|
缓存 测试技术 Python
解锁Python超能力:深入理解装饰器
解锁Python超能力:深入理解装饰器
236 2

推荐镜像

更多