Python类装饰器实现方法详解
本文实例讲述了Python类装饰器。分享给大家供大家参考,具体如下:
编写类装饰器
类装饰器类似于函数装饰器的概念,但它应用于类,它们可以用于管理类自身,或者用来拦截实例创建调用以管理实例。
单体类
由于类装饰器可以拦截实例创建调用,所以它们可以用来管理一个类的所有实例,或者扩展这些实例的接口。
下面的类装饰器实现了传统的单体编码模式,即最多只有一个类的一个实例存在。
instances={}#全局变量,管理实例 defgetInstance(aClass,*args): ifaClassnotininstances: instances[aClass]=aClass(*args) returninstances[aClass]#每一个类只能存在一个实例 defsingleton(aClass): defonCall(*args): returngetInstance(aClass,*args) returnonCall 为了使用它,装饰用来强化单体模型的类: @singleton#Person=singleton(Person) classPerson: def__init__(self,name,hours,rate): self.name=name self.hours=hours self.rate=rate defpay(self): returnself.hours*self.rate @singleton#Spam=singleton(Spam) classSpam: def__init__(self,val): self.attr=val bob=Person('Bob',40,10) print(bob.name,bob.pay()) sue=Person('Sue',50,20) print(sue.name,sue.pay()) X=Spam(42) Y=Spam(99) print(X.attr,Y.attr)
现在,当Person或Spam类稍后用来创建一个实例的时候,装饰器提供的包装逻辑层把实例构建调用指向了onCall,它反过来调用getInstance,以针对每个类管理并分享一个单个实例,而不管进行了多少次构建调用。
程序输出如下:
Bob400
Bob400
4242
在这里,我们使用全局的字典instances来保存实例,还有一个更好的解决方案就是使用Python3中的nonlocal关键字,它可以为每个类提供一个封闭的作用域,如下:
defsingleton(aClass): instance=None defonCall(*args): nonlocalinstance ifinstance==None: instance=aClass(*args) returninstance returnonCall
当然,我们也可以用类来编写这个装饰器——如下代码对每个类使用一个实例,而不是使用一个封闭作用域或全局表:
classsingleton: def__init__(self,aClass): self.aClass=aClass self.instance=None def__call__(self,*args): ifself.instance==None: self.instance=self.aClass(*args) returnself.instance
跟踪对象接口
类装饰器的另一个常用场景是每个产生实例的接口。类装饰器基本上可以在实例上安装一个包装器逻辑层,来以某种方式管理其对接口的访问。
前面,我们知道可以用__getattr__运算符重载方法作为包装嵌入到实例的整个对象接口的方法,以便实现委托编码模式。__getattr__用于拦截未定义的属性名的访问。如下例子所示:
classWrapper: def__init__(self,obj): self.wrapped=obj def__getattr__(self,attrname): print('Trace:',attrname) returngetattr(self.wrapped,attrname) >>>x=Wrapper([1,2,3]) >>>x.append(4) Trace:append >>>x.wrapped [1,2,3,4] >>> >>>x=Wrapper({'a':1,'b':2}) >>>list(x.keys()) Trace:keys ['b','a']
在这段代码中,Wrapper类拦截了对任何包装对象的属性的访问,打印出一条跟踪信息,并且使用内置函数getattr来终止对包装对象的请求。
类装饰器为编写这种__getattr__技术来包装一个完整接口提供了一个替代的、方便的方法。如下:
defTracer(aClass): classWrapper: def__init__(self,*args,**kargs): self.fetches=0 self.wrapped=aClass(*args,**kargs) def__getattr__(self,attrname): print('Trace:'+attrname) self.fetches+=1 returngetattr(self.wrapped,attrname) returnWrapper @Tracer classSpam: defdisplay(self): print('Spam!'*8) @Tracer classPerson: def__init__(self,name,hours,rate): self.name=name self.hours=hours self.rate=rate defpay(self): returnself.hours*self.rate food=Spam() food.display() print([food.fetches]) bob=Person('Bob',40,50) print(bob.name) print(bob.pay()) print('') sue=Person('Sue',rate=100,hours=60) print(sue.name) print(sue.pay()) print(bob.name) print(bob.pay()) print([bob.fetches,sue.fetches])
通过拦截实例创建调用,这里的类装饰器允许我们跟踪整个对象接口,例如,对其任何属性的访问。
Spam和Person类的实例上的属性获取都会调用Wrapper类中的__getattr__逻辑,由于food和bob确实都是Wrapper的实例,得益于装饰器的实例创建调用重定向,输出如下:
Trace:display
Spam!Spam!Spam!Spam!Spam!Spam!Spam!Spam!
[1]
Trace:name
Bob
Trace:pay
2000
Trace:name
Sue
Trace:pay
6000
Trace:name
Bob
Trace:pay
2000
[4,2]
示例:实现私有属性
如下的类装饰器实现了一个用于类实例属性的Private声明,也就是说,属性存储在一个实例上,或者从其一个类继承而来。不接受从装饰的类的外部对这样的属性的获取和修改访问,但是,仍然允许类自身在其方法中自由地访问那些名称。类似于Java中的private属性。
traceMe=False deftrace(*args): iftraceMe: print('['+''.join(map(str,args))+']') defPrivate(*privates): defonDecorator(aClass): classonInstance: def__init__(self,*args,**kargs): self.wrapped=aClass(*args,**kargs) def__getattr__(self,attr): trace('get:',attr) ifattrinprivates: raiseTypeError('privateattributefetch:'+attr) else: returngetattr(self.wrapped,attr) def__setattr__(self,attr,value): trace('set:',attr,value) ifattr=='wrapped':#这里捕捉对wrapped的赋值 self.__dict__[attr]=value elifattrinprivates: raiseTypeError('privateattributechange:'+attr) else:#这里捕捉对wrapped.attr的赋值 setattr(self.wrapped,attr,value) returnonInstance returnonDecorator if__name__=='__main__': traceMe=True @Private('data','size') classDoubler: def__init__(self,label,start): self.label=label self.data=start defsize(self): returnlen(self.data) defdouble(self): foriinrange(self.size()): self.data[i]=self.data[i]*2 defdisplay(self): print('%s=>%s'%(self.label,self.data)) X=Doubler('Xis',[1,2,3]) Y=Doubler('Yis',[-10,-20,-30]) print(X.label) X.display() X.double() X.display() print(Y.label) Y.display() Y.double() Y.label='Spam' Y.display() #这些访问都会引发异常 """ print(X.size()) print(X.data) X.data=[1,1,1] X.size=lambdaS:0 print(Y.data) print(Y.size())
这个示例运用了装饰器参数等语法,稍微有些复杂,运行结果如下:
[set:wrapped<__main__.Doublerobjectat0x03421F10>]
[set:wrapped<__main__.Doublerobjectat0x031B7470>]
[get:label]
Xis
[get:display]
Xis=>[1,2,3]
[get:double]
[get:display]
Xis=>[2,4,6]
[get:label]
Yis
[get:display]
Yis=>[-10,-20,-30]
[get:double]
[set:labelSpam]
[get:display]
Spam=>[-20,-40,-60]
更多关于Python相关内容可查看本站专题:《Python数据结构与算法教程》、《PythonSocket编程技巧总结》、《Python函数使用技巧总结》、《Python字符串操作技巧汇总》及《Python入门与进阶经典教程》
希望本文所述对大家Python程序设计有所帮助。