设为首页 收藏本站
查看: 1007|回复: 0

[经验分享] Python 装饰器学习以及实际使用场景实践

[复制链接]

尚未签到

发表于 2015-11-29 11:34:58 | 显示全部楼层 |阅读模式
前言
  前几天在看Flask框架,
对于非常神奇的 at 语法,不是非常的理解,回来补装饰器的功课。阅读很多的关于装饰器的文章,自己整理一下,适合自己的思路的方法和例子,与大家分享。  

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

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

2、装饰器的语法
  装饰器的语法如下:
  

当前Python的装饰器语法如下:  
@dec2
  
@dec1
  
def func(arg1, arg2, ...):
  ....
  return funx
  

  
上面的代码相当于:
  

  
def func(arg1, arg2, ...):
  pass
  
func = dec2(dec1(func))
  

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

3、装饰器的使用

3.1不带参数的装饰器
  定义装饰器非常的简单:
  

def deco(func):  """无参数调用decorator声明时必须有一个参数,这个参数将接收要装饰的方法"""
  print "before myfunc() called."
  func()
  print "after myfunc() called."
  return func
  

  
@deco
  
def myfunc():
  print " myfunc() called."
  

  

  
myfunc()
  
myfunc()
  

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

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

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

def deco(func):  """无参数调用decorator声明时必须有一个参数,这个参数将接收要装饰的方法"""
  def _deco():
  print "before myfunc() called."
  func()
  print "after myfunc() called."
  #return func 不需要返回func
  retrun _deco
  
@deco
  
def myfunc():
  print " myfunc() called."
  return 'OK'
  

  
myfunc()
  
myfunc()
  

  函数输出如下:
  

before myfunc() called.  myfunc() called.
  after myfunc() called.
  
before myfunc() called.
  myfunc() called.
  after myfunc() called.
  

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

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

def deco(func):  def _deco(a, b):
  print("before myfunc() called.")
  ret = func(a, b)
  print("  after myfunc() called. result: %s" % ret)
  return ret
  
return _deco
  

  
@deco
  
def myfunc(a, b):
  print(" myfunc(%s,%s) called." % (a, b))
  return a + b
  
  
myfunc(1, 2)
  
myfunc(3, 4)
  

  输出:
  before myfunc() called.
  myfunc() called.
  After myfunc() called. result: 3
  

before myfunc() called.  
myfunc() called.
  
After myfunc() called. result: 7
  

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

3.3装饰器带参数
  

def decoWithArgs(arg):  
"""由于有参数的decorator函数在调用时只会使用应用时的参数而不接收被装饰的函数做为参数,
  所以必须返回一个decorator函数, 由它对被装饰的函数进行封装处理"""
  
def newDeco(func):    #定义一个新的decorator函数
  def replaceFunc():    #在decorator函数里面再定义一个内嵌函数,由它封装具体的操作
  print "Enter decorator %s" %arg    #进行额外操作
  return func()    #对被装饰函数进行调用
  return replaceFunc
  
return newDeco    #返回一个新的decorator函数
  

  
@decoWithArgs("demo")
  
def MyFunc():    #应用@decoWithArgs修饰的方法
  print "Enter MyFunc"
  

  
MyFunc()    #调用被装饰的函数
  

  输出:
  nter decorator demo
  Enter MyFunc
  这个情形适用于原来的函数没有参数,新增加打印的情况。常见适用的地方是增加函数的打印日志。

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

from threading import Thread  

  
def async(f):
  def wrapper(*args, **kwargs):
  thr = Thread(target = f, args = args, kwargs = kwargs)
  thr.start()
  return wrapper
  

  
@async
  
def send_async_email(msg):
  mail.send(msg)
  

  
def send_email(subject, sender, recipients, text_body, html_body):
  msg = Message(subject, sender = sender, recipients = recipients)
  msg.body = text_body
  msg.html = html_body
  send_async_email(msg)
  

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

3.5让装饰器带类参数
  

class locker:  def __init__(self):
  print("locker.__init__() should be not called.")
  
  @staticmethod
  def acquire():
  print("locker.acquire() called.(这是静态方法)")
  
  @staticmethod

  def>  print("  locker.release() called.(不需要对象实例)")
  
  
def deco(cls):
  '''cls 必须实现acquire和release静态方法'''
  def _deco(func):
  def __deco():
  print("before %s called [%s]." % (func.__name__, cls))
  cls.acquire()
  try:
  return func()
  finally:
  cls.release()
  return __deco
  return _deco
  
  
@deco(locker)
  
def myfunc():
  print(" myfunc() called.")
  
  
myfunc()
  
myfunc()
  

  输出为:
  

before myfunc called [__main__.locker].  
locker.acquire() called.(this is staticmethon)
  
myfunc() called.
  locker.release() called.(do't need object )
  

  
before myfunc called [__main__.locker].
  
locker.acquire() called.(this is staticmethon)
  
myfunc() called.
  locker.release() called.(do't need object )
  

装饰器总结

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


参考文章
  感谢以下几位大神:
  http://www.cnblogs.com/rhcad/archive/2011/12/21/2295507.html
  http://www.cnblogs.com/Jifangliang/archive/2008/07/22/1248313.html
  http://www.cnblogs.com/vamei/archive/2013/02/16/2820212.html

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-144876-1-1.html 上篇帖子: 用python管理自己的音乐文件夹 下篇帖子: 在SAE上布置Python+Django+MySQL的过程
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表