Reputation: 45281
I have a decorator class validatekeys()
and a Node3D()
class.
The intention is for Node3D
to hold coordinate values of x, y, and z which are retrieved using a @property
decorator, and can be set using either a @coords.setter
decorator (which calls set_coords()
) or directly using set_coords()
which is itself decorated with validatekeys()
. I am using decorators to accomplish this so that I can add other classes later, like Node2D()
, for example.
Code:
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
However, part of the output is not as expected:
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
It looks like the self.__dict__
is not being updated correctly. However, I've been unable to figure out how to fix this. Any suggestions?
Note that although I am certainly interested in alternative formulations/approaches on solving this problem (validating keys input to a setter), this is mostly a learning exercise to understand how decorators, classes, etc etc work.
Upvotes: 0
Views: 126
Reputation: 1123420
Your decorator is updating the wrong __dict__
; self
in your decorator __call__
is the decorator object itself.
You need to extract the bound self
argument from the called wrapper:
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)
You can give your wrapped_f()
an explicit first argument too:
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)
Here instance
is bound to the Node3D
instance. Note that there is no hard requirement to name this variable self
; that is just a convention.
Upvotes: 2
Reputation: 251438
The self
in your __call__
refers to the validator, not the Node3D object, so the validator is updating its own __dict__
. Try this instead:
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
Here I've renamed the self
in the __call__
to validator_self
to make it clear that that self refers to the validator. I added a self
to the wrapper function; this self
will refer to the "real" self of the Node3D object where the validated method is.
Upvotes: 1