2014-10-29 48 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对象的“真实”自我。