Rick
Rick

Reputation: 45231

Using decorators as class attributes instead of instance attributes

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.

Upvotes: 2

Views: 88

Answers (1)

Rick
Rick

Reputation: 45231

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.

Upvotes: 2

Related Questions