使用装饰器验证属性名称

时间:2014-10-29 22:05:16

标签: python properties decorator python-decorators

我有一个装饰器类validatekeys()和一个Node3D()类。

意图是Node3D保存使用@property装饰器检索的x,y和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__

看起来{{1}}未正确更新。但是,我一直无法弄清楚如何解决这个问题。有什么建议吗?

请注意,虽然我当然对解决此问题的替代公式/方法感兴趣(验证输入到setter的键输入),但这主要是学习装饰器,类等等如何工作的学习练习。

2 个答案:

答案 0 :(得分: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 :(得分:1)

self中的__call__引用验证器,而不是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

我已将self中的__call__重命名为validator_self,以明确该自我引用验证者。我在包装函数中添加了self;这个self将引用经过验证的方法所在的Node3D对象的“真实”自我。