2

I have the following classes.

Validator is a decorator that receives a class which defines validation criteria for a decorated function. ValidateKeys is the validation criteria for this example. Node2D is a class using validation criteria.

class Validator(object):
    def __init__(self, TheValidator, *args, **kwargs):
        self.validator = TheValidator(*args,**kwargs)
    def __call__(self,f):
        def wrapped_f(instance, *args,**kwargs):
            self.TheValidator(instance, *args, **kwargs)
            return f(instance,*args,**kwargs)
        return wrapped_f

class ValidateKeys(object):
    def __init__(self,*keysIterable):
        self.validkeys = keysIterable
    def __call__(self, instance, **kwargs):
        for a in kwargs:
            if not a in self.validkeys:
                raise Exception()
        instance.__dict__.update(kwargs)

class Node2D(object):
    @property
    def coords(self):
        return self.__dict__
    @coords.setter
    def coords(self,Coords):
        self.set_coords(**Coords)
    @Validator(ValidateKeys, 'x','y')
    def set_coords(self,**Coords):
        pass

From what I understand, as things are written here, every instance of Node2D will produce a duplicate Validator (as will any other class decorated with Validator) and ValidateKeys.

EDIT: THIS IS WRONG! See answer below.

Note that this is primarily a learning exercise for me and although I would be interested in hearing criticisms/suggestions for improving my over all approach, my primary goal is to learn more about how to use decorators effectively.

Also note that I normally would not use capitalization for a decorator class but am using it here since it makes it easier to read on SO.

9
  • 2
    This might be better suited for codereview.stackexchange.com. I will note this: Validator is a class with only two methods, one of which is __init__; it should probably just be a function instead. (See the talk "Stop Writing Classes" from PyCon 2012. Commented Oct 30, 2014 at 12:55
  • This question appears to be off-topic because it is about code review, not a specific problem. Commented Oct 30, 2014 at 12:57
  • I will remove the part about getting rid of duplicate class definitions then. The part about making Validator etc into a class object is very specific. Commented Oct 30, 2014 at 12:59
  • 1
    Especially as it's not even true that each instance would get a new instance of the validators. Decorators are evaluated at define time, not on instantiation. You could easily verify this by putting logging in the decorator __init__ methods. Commented Oct 30, 2014 at 13:03
  • 1
    I meant, all the questions posted above, not all your questions on the site. Commented Oct 30, 2014 at 13:49

1 Answer 1

2

My assumption was incorrect.

As things are written, only one instance of Validator and ValidateKeys is created per class. I did not realize that the line @Validator(ValidateKeys, 'x','y') only runs once (at the time of class definition) and not at instance creation.

I should have realized this, since decorator expressions appear at the same level of hierarchy as class attributes, e.g.:

class MyClass():
    class_attribute = None #only one class_attribute is created
    @decorator             #only one decorator (i.e., decorated method) is created
    def method():
        pass

Test:

class Validator(object):
    def __init__(self, TheValidator, *args, **kwargs):
        print("New Validator Object")
        self.TheValidator = TheValidator(*args,**kwargs)
    def __call__(self,f):
        def wrapped_f(instance, *args,**kwargs):
            self.TheValidator(instance, *args, **kwargs)
            return f(instance,*args,**kwargs)
        return wrapped_f

class ValidateKeys(object):
    def __init__(self,*keysIterable):
        print("New ValidateKeys Object")
        self.validkeys = keysIterable
    def __call__(self, instance, **kwargs):
        for a in kwargs:
            if not a in self.validkeys:
                raise Exception()
        instance.__dict__.update(kwargs)

class Node2D(object):
    @property
    def coords(self):
        return self.__dict__
    @coords.setter
    def coords(self,Coords):
        self.set_coords(**Coords)
    @Validator(ValidateKeys, 'x','y')
    def set_coords(self,**Coords):
        pass

n1 = Node2D()
n2 = Node2D()
n1.setcoords(x=1,y=2)
n1.coords

Output:

'New Validator Object'    #<-- Seen only once when module is loaded (class defined)
'New ValidateKeys Object' #<-- Seen only once when module is loaded (class defined)
'{'x': 1, 'y': 2}'

I do not have the problem I thought I had. Thanks to all for the help.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.