国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Python > 正文

Python類裝飾器實現方法詳解

2020-01-04 13:45:01
字體:
來源:轉載
供稿:網友

本文實例講述了Python類裝飾器。分享給大家供大家參考,具體如下:

編寫類裝飾器

類裝飾器類似于函數裝飾器的概念,但它應用于類,它們可以用于管理類自身,或者用來攔截實例創建調用以管理實例。

單體類

由于類裝飾器可以攔截實例創建調用,所以它們可以用來管理一個類的所有實例,或者擴展這些實例的接口。

下面的類裝飾器實現了傳統的單體編碼模式,即最多只有一個類的一個實例存在。

instances = {} # 全局變量,管理實例def getInstance(aClass, *args):  if aClass not in instances:    instances[aClass] = aClass(*args)  return instances[aClass]   #每一個類只能存在一個實例def singleton(aClass):  def onCall(*args):    return getInstance(aClass,*args)  return onCall為了使用它,裝飾用來強化單體模型的類:@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 = valbob = 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,以針對每個類管理并分享一個單個實例,而不管進行了多少次構建調用。

程序輸出如下:

Bob 400
Bob 400
42 42

在這里,我們使用全局的字典instances來保存實例,還有一個更好的解決方案就是使用Python3中的nonlocal關鍵字,它可以為每個類提供一個封閉的作用域,如下:

def singleton(aClass): instance = None def onCall(*args): nonlocal instance if instance == None:  instance = aClass(*args) return instance return onCall

當然,我們也可以用類來編寫這個裝飾器——如下代碼對每個類使用一個實例,而不是使用一個封閉作用域或全局表:

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

跟蹤對象接口

類裝飾器的另一個常用場景是每個產生實例的接口。類裝飾器基本上可以在實例上安裝一個包裝器邏輯層,來以某種方式管理其對接口的訪問。

前面,我們知道可以用__getattr__運算符重載方法作為包裝嵌入到實例的整個對象接口的方法,以便實現委托編碼模式。__getattr__用于攔截未定義的屬性名的訪問。如下例子所示:

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類攔截了對任何包裝對象的屬性的訪問,打印出一條跟蹤信息,并且使用內置函數getattr來終止對包裝對象的請求。

類裝飾器為編寫這種__getattr__技術來包裝一個完整接口提供了一個替代的、方便的方法。如下:

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@Tracerclass Spam:  def display(self):    print('Spam!'*8)@Tracerclass Person:  def __init__(self,name,hours,rate):    self.name = name    self.hours = hours    self.rate = rate  def pay(self):    return self.hours * self.ratefood = 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 = Falsedef 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': # 這里捕捉對wrapped的賦值          self.__dict__[attr] = value        elif attr in privates:          raise TypeError('private attribute change:'+attr)        else: # 這里捕捉對wrapped.attr的賦值          setattr(self.wrapped,attr,value)    return onInstance  return onDecoratorif __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()  # 這些訪問都會引發異常  """  print(X.size())  print(X.data)  X.data = [1,1,1]  X.size = lambda S:0  print(Y.data)  print(Y.size())

這個示例運用了裝飾器參數等語法,稍微有些復雜,運行結果如下:

[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]

希望本文所述對大家Python程序設計有所幫助。


注:相關教程知識閱讀請移步到python教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 陈巴尔虎旗| 垣曲县| 云梦县| 文登市| 永州市| 永和县| 青神县| 六安市| 屯昌县| 武威市| 图片| 本溪| 普格县| 封开县| 洞头县| 贵州省| 安吉县| 祁连县| 邹平县| 信丰县| 平安县| 正蓝旗| 德庆县| 隆安县| 昌江| 海伦市| 平顶山市| 宜章县| 毕节市| 阳城县| 乌兰浩特市| 旺苍县| 志丹县| 眉山市| 大悟县| 铜川市| 永吉县| 新密市| 扶余县| 全椒县| 长子县|