Python類(lèi)裝飾器實(shí)現(xiàn)方法詳解
本文實(shí)例講述了Python類(lèi)裝飾器。分享給大家供大家參考,具體如下:
編寫(xiě)類(lèi)裝飾器
類(lèi)裝飾器類(lèi)似于函數(shù)裝飾器的概念,但它應(yīng)用于類(lèi),它們可以用于管理類(lèi)自身,或者用來(lái)攔截實(shí)例創(chuàng)建調(diào)用以管理實(shí)例。
單體類(lèi)
由于類(lèi)裝飾器可以攔截實(shí)例創(chuàng)建調(diào)用,所以它們可以用來(lái)管理一個(gè)類(lèi)的所有實(shí)例,或者擴(kuò)展這些實(shí)例的接口。
下面的類(lèi)裝飾器實(shí)現(xiàn)了傳統(tǒng)的單體編碼模式,即最多只有一個(gè)類(lèi)的一個(gè)實(shí)例存在。
instances = {} # 全局變量,管理實(shí)例
def getInstance(aClass, *args):
if aClass not in instances:
instances[aClass] = aClass(*args)
return instances[aClass] #每一個(gè)類(lèi)只能存在一個(gè)實(shí)例
def singleton(aClass):
def onCall(*args):
return getInstance(aClass,*args)
return onCall
為了使用它,裝飾用來(lái)強(qiáng)化單體模型的類(lèi):
@singleton # Person = singleton(Person)
class Person:
def __init__(self,name,hours,rate):
self.name = name
self.hours = hours
self.rate = rate
def pay(self):
return self.hours * self.rate
@singleton # Spam = singleton(Spam)
class Spam:
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)
現(xiàn)在,當(dāng)Person或Spam類(lèi)稍后用來(lái)創(chuàng)建一個(gè)實(shí)例的時(shí)候,裝飾器提供的包裝邏輯層把實(shí)例構(gòu)建調(diào)用指向了onCall,它反過(guò)來(lái)調(diào)用getInstance,以針對(duì)每個(gè)類(lèi)管理并分享一個(gè)單個(gè)實(shí)例,而不管進(jìn)行了多少次構(gòu)建調(diào)用。
程序輸出如下:
Bob 400
Bob 400
42 42
在這里,我們使用全局的字典instances來(lái)保存實(shí)例,還有一個(gè)更好的解決方案就是使用Python3中的nonlocal關(guān)鍵字,它可以為每個(gè)類(lèi)提供一個(gè)封閉的作用域,如下:
def singleton(aClass): instance = None def onCall(*args): nonlocal instance if instance == None: instance = aClass(*args) return instance return onCall
當(dāng)然,我們也可以用類(lèi)來(lái)編寫(xiě)這個(gè)裝飾器——如下代碼對(duì)每個(gè)類(lèi)使用一個(gè)實(shí)例,而不是使用一個(gè)封閉作用域或全局表:
class singleton: def __init__(self,aClass): self.aClass = aClass self.instance = None def __call__(self,*args): if self.instance == None: self.instance = self.aClass(*args) return self.instance
跟蹤對(duì)象接口
類(lèi)裝飾器的另一個(gè)常用場(chǎng)景是每個(gè)產(chǎn)生實(shí)例的接口。類(lèi)裝飾器基本上可以在實(shí)例上安裝一個(gè)包裝器邏輯層,來(lái)以某種方式管理其對(duì)接口的訪問(wèn)。
前面,我們知道可以用__getattr__運(yùn)算符重載方法作為包裝嵌入到實(shí)例的整個(gè)對(duì)象接口的方法,以便實(shí)現(xiàn)委托編碼模式。__getattr__用于攔截未定義的屬性名的訪問(wèn)。如下例子所示:
class Wrapper:
def __init__(self,obj):
self.wrapped = obj
def __getattr__(self,attrname):
print('Trace:',attrname)
return getattr(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類(lèi)攔截了對(duì)任何包裝對(duì)象的屬性的訪問(wèn),打印出一條跟蹤信息,并且使用內(nèi)置函數(shù)getattr來(lái)終止對(duì)包裝對(duì)象的請(qǐng)求。
類(lèi)裝飾器為編寫(xiě)這種__getattr__技術(shù)來(lái)包裝一個(gè)完整接口提供了一個(gè)替代的、方便的方法。如下:
def Tracer(aClass):
class Wrapper:
def __init__(self,*args,**kargs):
self.fetches = 0
self.wrapped = aClass(*args,**kargs)
def __getattr__(self,attrname):
print('Trace:'+attrname)
self.fetches += 1
return getattr(self.wrapped,attrname)
return Wrapper
@Tracer
class Spam:
def display(self):
print('Spam!'*8)
@Tracer
class Person:
def __init__(self,name,hours,rate):
self.name = name
self.hours = hours
self.rate = rate
def pay(self):
return self.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])
通過(guò)攔截實(shí)例創(chuàng)建調(diào)用,這里的類(lèi)裝飾器允許我們跟蹤整個(gè)對(duì)象接口,例如,對(duì)其任何屬性的訪問(wèn)。
Spam和Person類(lèi)的實(shí)例上的屬性獲取都會(huì)調(diào)用Wrapper類(lèi)中的__getattr__邏輯,由于food和bob確實(shí)都是Wrapper的實(shí)例,得益于裝飾器的實(shí)例創(chuàng)建調(diào)用重定向,輸出如下:
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]
示例:實(shí)現(xiàn)私有屬性
如下的類(lèi)裝飾器實(shí)現(xiàn)了一個(gè)用于類(lèi)實(shí)例屬性的Private聲明,也就是說(shuō),屬性存儲(chǔ)在一個(gè)實(shí)例上,或者從其一個(gè)類(lèi)繼承而來(lái)。不接受從裝飾的類(lèi)的外部對(duì)這樣的屬性的獲取和修改訪問(wèn),但是,仍然允許類(lèi)自身在其方法中自由地訪問(wèn)那些名稱。類(lèi)似于Java中的private屬性。
traceMe = False
def trace(*args):
if traceMe:
print('['+ ' '.join(map(str,args))+ ']')
def Private(*privates):
def onDecorator(aClass):
class onInstance:
def __init__(self,*args,**kargs):
self.wrapped = aClass(*args,**kargs)
def __getattr__(self,attr):
trace('get:',attr)
if attr in privates:
raise TypeError('private attribute fetch:'+attr)
else:
return getattr(self.wrapped,attr)
def __setattr__(self,attr,value):
trace('set:',attr,value)
if attr == 'wrapped': # 這里捕捉對(duì)wrapped的賦值
self.__dict__[attr] = value
elif attr in privates:
raise TypeError('private attribute change:'+attr)
else: # 這里捕捉對(duì)wrapped.attr的賦值
setattr(self.wrapped,attr,value)
return onInstance
return onDecorator
if __name__ == '__main__':
traceMe = True
@Private('data','size')
class Doubler:
def __init__(self,label,start):
self.label = label
self.data = start
def size(self):
return len(self.data)
def double(self):
for i in range(self.size()):
self.data[i] = self.data[i] * 2
def display(self):
print('%s => %s'%(self.label,self.data))
X = Doubler('X is',[1,2,3])
Y = Doubler('Y is',[-10,-20,-30])
print(X.label)
X.display()
X.double()
X.display()
print(Y.label)
Y.display()
Y.double()
Y.label = 'Spam'
Y.display()
# 這些訪問(wèn)都會(huì)引發(fā)異常
"""
print(X.size())
print(X.data)
X.data = [1,1,1]
X.size = lambda S:0
print(Y.data)
print(Y.size())
這個(gè)示例運(yùn)用了裝飾器參數(shù)等語(yǔ)法,稍微有些復(fù)雜,運(yùn)行結(jié)果如下:
[set: wrapped <__main__.Doubler object at 0x03421F10>]
[set: wrapped <__main__.Doubler object at 0x031B7470>]
[get: label]
X is
[get: display]
X is => [1, 2, 3]
[get: double]
[get: display]
X is => [2, 4, 6]
[get: label]
Y is
[get: display]
Y is => [-10, -20, -30]
[get: double]
[set: label Spam]
[get: display]
Spam => [-20, -40, -60]
更多關(guān)于Python相關(guān)內(nèi)容可查看本站專題:《Python數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Python Socket編程技巧總結(jié)》、《Python函數(shù)使用技巧總結(jié)》、《Python字符串操作技巧匯總》及《Python入門(mén)與進(jìn)階經(jīng)典教程》
希望本文所述對(duì)大家Python程序設(shè)計(jì)有所幫助。
相關(guān)文章
Python高階函數(shù)、常用內(nèi)置函數(shù)用法實(shí)例分析
這篇文章主要介紹了Python高階函數(shù)、常用內(nèi)置函數(shù)用法,結(jié)合實(shí)例形式分析了Python高階函數(shù)與常用內(nèi)置函數(shù)相關(guān)功能、原理、使用技巧與操作注意事項(xiàng),需要的朋友可以參考下2019-12-12
Python 實(shí)現(xiàn)字符串中指定位置插入一個(gè)字符
下面小編就為大家分享一篇Python 實(shí)現(xiàn)字符串中指定位置插入一個(gè)字符,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-05-05
python+Splinter實(shí)現(xiàn)12306搶票功能
這篇文章主要為大家詳細(xì)介紹了python+Splinter實(shí)現(xiàn)12306搶票功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-09-09
python進(jìn)程間通信Queue工作過(guò)程詳解
這篇文章主要介紹了python進(jìn)程間通信Queue工作過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11
python實(shí)現(xiàn)基于SVM手寫(xiě)數(shù)字識(shí)別功能
這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)基于SVM手寫(xiě)數(shù)字識(shí)別功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01
Python socket C/S結(jié)構(gòu)的聊天室應(yīng)用實(shí)現(xiàn)
這篇文章主要介紹了Python socket C/S結(jié)構(gòu)的聊天室應(yīng)用實(shí)現(xiàn),需要的朋友可以參考下2014-11-11
使用Python求解帶約束的最優(yōu)化問(wèn)題詳解
今天小編就為大家分享一篇使用Python求解帶約束的最優(yōu)化問(wèn)題詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-02-02
OpenCV特征提取與檢測(cè)之Shi-Tomasi角點(diǎn)檢測(cè)器
在角點(diǎn)檢測(cè)的世界里哈瑞斯無(wú)疑是最重要的方法之一,但Shi-Tomasi作為改進(jìn)的算法也有很大應(yīng)用場(chǎng)景,尤其是動(dòng)態(tài)跟蹤用的還比較多,這篇文章主要給大家介紹了關(guān)于OpenCV特征提取與檢測(cè)之Shi-Tomasi角點(diǎn)檢測(cè)器的相關(guān)資料,需要的朋友可以參考下2021-08-08
Python實(shí)現(xiàn)3行代碼解簡(jiǎn)單的一元一次方程
這篇文章主要介紹了Python實(shí)現(xiàn)3行代碼解簡(jiǎn)單的一元一次方程,很適合Python初學(xué)者學(xué)習(xí)借鑒,需要的朋友可以參考下2014-08-08

