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

[经验分享] Python的Decorator 杂谈

[复制链接]

尚未签到

发表于 2015-4-20 08:47:05 | 显示全部楼层 |阅读模式
  
  Decorator 这个特性,顾名思义,这是一个内置的装饰器模式的实现,利用这个特征在Python里实现AOP易如反掌,这里我分3个部分来说明Decorator 是什么,怎么写,怎么用
  首先我们要知道Decorator 是什么。
  Decorator r是装饰器模式的实现,那么 简单的来说就是用一个新的对象来替换掉原有的对象,新的对象包含原有的对象,我们可以像调用原有对象一样的来调用新对象,且新对象的创建我们可以控制,所以可以在调用原有对象的时候实现控制。或者将原有对象处理后原样返回。
  我们来举一个例子
  假设有一个函数
  


1 def foo():pass  
  
  这个方法什么都不做。现在我们定义一个装饰器来扩展这个函数
  


1 def before(func):
2     def wapper(*args):
3          print "print before invoke foo"
4          return func(*args)
5     return wapper  
  
  这个装饰器方法通过参数func得到被装饰的对象foo 函数(注意:在Python中一切都是对象,包括类和函数),然后定义了一个新的函数来代替原有的函数,并且返回新函数,由于闭包的特性,在定义的新函数内部能够访问到参数func,所以我们在新函数内部,执行被装饰的函数,并且将被装饰的函数的返回值作为新函数的返回值。
  


1 @before
2 def foo():pass  
  
  如上面这般就能用before装饰器来装饰foo函数了。我们执行foo(),就会显示 print before invoke foo。
  这是最简单的情况。 下面我们来看装饰函数的Decorator ,装饰类的Decorator ,带参数的Decorator ,用类的方式来写Decorator
  在上面的例子我们知道了装饰器的基本形式,被装饰的对象通过第一层函数的参数传入,那么如果装饰的是一个函数,那么第一个参数就是函数对象,如果是类那么就是类的对象。
  函数的装饰我们看过了,下面来看一个装饰类的。通常我们调用类的形式都是实例化这个类,所以我们装饰的也是实例化的过程。
  

DSC0000.gif DSC0001.gif 代码

1 def single(cls):
2     ins={}                         #存储所有的类的唯一实例
3     def getins(*args):             #用于替代类的装饰方法,返回相当于是类的工厂函数。
4         if cls not in ins:         #如果类的实例不在缓存中
5             ins[cls]=cls(*args)    #实例化对象,加入缓存
6         return ins[cls]            #返回缓存中的唯一实例
7     return getins                  #返回工厂函数  
  
  这个例子我们可以看出如果用一个函数来代替类 ,那么结果就是我们可以扩展一个类的构造过程。当然在这里我们不知能够扩展实例化 的过程,扩展类对象本身也是没有问题的。
  

  如果我们想在装饰一个对象的时候加上参数怎么办呢?那么就需要用另外一种写法了。我们来看看:
  


1 def author(name):
2     def add(cls):
3         setattr(cls,"__author__",name)
4         return cls
5     return add  
  
  这个装饰器为一个类对象加上 __author__的值
  如果是想要给类的实例加上 __author__ 的值就要像下面这么写
  


1 def author(name):
2     def add(cls):
3         def wapper(*args):
4             ins=cls(*args)
5             setattr(ins,"__author__",name)
6             return ins
7         return wapper
8     return add  
  
  为了给类的实例对象加入值我们就不能返回类对象本身而是返回实例化类的方法
  我们可以看到上面的装饰器都是一个个的函数,一般来说我们利用高阶函数和闭包的特性来延迟具体操作的实现,不过闭包也有自身的局限性,所以我们还可以用Python的类形的特征来用类来实现装饰器。
  我们将装饰器可以看作是一次或者是两次函数调用,如果不包含参数就是调用一次,如果是包含参数就是两次,一次是传递参数,一次是传递要装饰的对象。而Python的类实例化其实和函数调用的形式差不多,所以我们可以用类的构造器来实现第一次调用,而Python的类特殊函数中有一个__call__函数可以使类实例变成可以和函数一样的调用,这样又可以实现第二次调用,我们把不带参数的和带参数的分别用类来实现一次:
  不带参数:
  


1 class before:
2     def __init__(self,func):
3         def wapper(*args):
4             print "before invoke"
5             return func(*args)
6         return wapper  
  
  

  带参数
  

代码

1 class author:
2     def __init__(self,name):
3         self._name=name
4     def __call__(self,cls):
5         def wapper(*args):
6             ins=cls(*args)
7             setattr(ins,"__author__",self._name)
8             return ins
9         return wapper  
  
  

  刚才我们说到了装饰器其实是系统自动发起的两次函数调用,但是系统在什么时候调用的呢?是在你import这个模块的时候。我们可以用下面的例子来实验
  

代码

1 class author:
2     def __init__(self,name):
3         print "first invoke in __init__"
4         self._name=name
5     def __call__(self,cls):
6         print "second invoke in __call__"
7         def wapper(*args):
8             ins=cls(*args)
9             setattr(ins,"__author__",self._name)
10             return ins
11         return wapper
12
13 @author
14 class A:pass  
  
  将上面的代码保存到一个py文件中,然后新建另外一个py文件,import 这个模块后不做任何操作,然后就能看到
  first invoke in __init__
  second invoke in __call__
  这两行
  利用这个特征我们可以很轻松实现很多特殊的效果,比如生成Django的Url映射。
  然后我们还要注意Decorator 的这个特性可能引发的问题,因为其实很多时候我们是返回了新的对象替换了原有对象,而且是在import后,这样子会造成很多功能,比如pydoc等在使用了装饰器后就不好用了,因为得到的文档其实是新对象的,所以你在用PyDoc自动生成文档的时候就得不到原有对象的__doc__信息,所以在实际的使用场景我们还有在返回前将__doc__,__name__等替换过来。比如
  

代码

1 class before:
2     def __init__(self,func):
3         def wapper(*args):
4             print "before invoke"
5             return func(*args)
6         wapper.__name__=func.__name__
7         wapper.__doc__=func.__doc__
8         return wapper  
  
  

  相信通过本文你应该清楚Decorator 的基本特征了,剩下的就是大家一起来发掘更多的用法了
  
  

运维网声明 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-58638-1-1.html 上篇帖子: Python字典 (dictionary) 下篇帖子: python基础学习笔记(十)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

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

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

扫描微信二维码查看详情

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


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


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


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



合作伙伴: 青云cloud

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