2014-10-29 45 views
0

我有一個裝飾器類validatekeys()Node3D()類。使用裝飾器驗證屬性名稱

的意圖是用於Node3D保持座標的x,y的值,並且其使用的是@property裝飾檢索Z,並且可以使用一個@coords.setter裝飾(它調用set_coords())或直接使用set_coords()其本身的裝飾被設置與validatekeys()。我正在使用裝飾器來完成此操作,以便以後可以添加其他類,例如Node2D()

代碼:

class validatekeys(object): 
    def __init__(self,*keysIterable): 
     self.validkeys = [] 
     for k in keysIterable: 
      self.validkeys.append(k) 
    def __call__(self,f): 
     def wrapped_f(*args,**kwargs): 
      for a in kwargs: 
       if not a in self.validkeys: 
        raise Exception() 
      self.__dict__.update(kwargs) 
      return f(self,**kwargs) 
     return wrapped_f 

class Node3D(object): 
    @property 
    def coords(self): 
     return self.__dict__ 
    @coords.setter 
    def coords(self,Coords): 
     self.set_coords(**Coords) 
    @validatekeys('x','y','z') 
    def set_coords(self,**Coords): 
     pass 

但是,並不如預期輸出的一部分:

n = Node2D() 
n.coords    #{} <--expected 
n.set_coords(x=1,y=2) 
n.coords    #{} <--not expected 
n.set_coords(a=1,b=2) #Exception <--expected 

它看起來像self.__dict__沒有被正確地更新。但是,我一直無法弄清楚如何解決這個問題。有什麼建議麼?

請注意,雖然我當然對解決這個問題的替代方案/方法感興趣(驗證輸入給setter的鍵輸入),但這主要是一個學習練習,以瞭解裝飾器,類等的工作原理。

回答

2

您的裝飾器更新了錯誤__dict__; self在您的裝飾器__call__裝飾器對象本身

您需要從所謂的包裝提取界self說法:

def wrapped_f(*args, **kwargs): 
    for a in kwargs: 
     if not a in self.validkeys: 
      raise Exception() 
    instance = args[0] 
    instance.__dict__.update(kwargs) 
    return f(*args, **kwargs) 

你可以給你的wrapped_f()一個明確的第一個參數也:

def wrapped_f(instance, *args, **kwargs): 
    for a in kwargs: 
     if not a in self.validkeys: 
      raise Exception() 
    instance.__dict__.update(kwargs) 
    return f(instance, *args, **kwargs) 

這裏instance綁定到Node3D實例。請注意,命名此變量並不難,請參閱self;那只是一個慣例。

+1

如果給'wrapped_f'一個明確的第一個參數,它需要有一個不同於驗證器的名稱,以避免對其進行遮蔽。它需要訪問驗證程序自身以檢索存儲在驗證程序中而不是Node3D對象上的有效密鑰列表。 – BrenBarn 2014-10-29 22:17:01

+0

@BrenBarn:darn,在那裏錯過了'self.validkeys'。 – 2014-10-29 22:18:57

+0

選擇此爲正確的答案,因爲它絕對,肯定,清晰。謝謝。 – 2014-10-29 22:25:30

1

您的__call__中的self引用驗證程序,而不是Node3D對象,因此驗證程序正在更新自己的__dict__。試試這個:

class validatekeys(object): 
    def __init__(self,*keysIterable): 
     self.validkeys = [] 
     for k in keysIterable: 
      self.validkeys.append(k) 
    def __call__(validator_self,f): 
     def wrapped_f(self, *args,**kwargs): 
      for a in kwargs: 
       if not a in validator_self.validkeys: 
        raise Exception() 
      self.__dict__.update(kwargs) 
      return f(self, *args, **kwargs) 
     return wrapped_f 

在這裏,我改名的__call__selfvalidator_self,使之清楚,自我是指驗證。我向包裝函數添加了一個self;這個self將引用經過驗證的方法所在的Node3D對象的「真實」自我。