0

so I think I've misunderstood my implementation of __getattribute__ I've created this class:

    class Model:

        schema = [
            'foo',
            'bar'
        ]
        values = {}

        def __init__(self, input_values=None):
            if input_values is not None:
                self.validate_input(input_values)

        def __call__(self, *args, **kwargs):
            if args[0] is not None:
                self.validate_input(args[0])

        def __iter__(self):
            for key, value in self.values.items():
                yield (key, value)

        def __getattribute__(self, item):
            if item in object.__getattribute__(self, 'values'):
                return object.__getattribute__(self, 'values')[item]
            return object.__getattribute__(self, item)

        def validate_input(self, input_values):
            for field, value in input_values.items():
                if field in self.schema:
                    self.values[field] = value

When instantiating the class, a dict of values is passed which will then be validated and stored within the 'values' property. I want these values to be accessed directly from the object so I've set up __getattribute__, this is my desired behaviour:

    m = Model({'foo': 'test', 'bar': 123})
    print(m.foo + ' ' + str(m.bar)) //prints "test 123"

Which works, however I must be misunderstanding something because when I try to populate a list of these objects and then access them from the list, each element returns the same value. For example:

    people = [
        {'firstname': 'brian', 'lastname': 'smith'},
        {'firstname': 'adrian', 'lastname': 'holmes'},
        {'firstname': 'emily', 'lastname': 'mcleod'},
    ]

    test_models = []
    for person in people:
        m = Model({'foo': person['firstname'], 'bar': person['lastname']})
        test_models.append(m)

    for model in test_models:
        print(model.values)

will print:

    {'foo': 'emily', 'bar': 'mcleod'}
    {'foo': 'emily', 'bar': 'mcleod'}
    {'foo': 'emily', 'bar': 'mcleod'}

I suspect this has something to do with the way I'm using 'object' inside __getattribute__? Or how I'm instantiating the Model class inside the loop?

Dioralop
  • 165
  • 1
  • 9

1 Answers1

1

The problem not in __getattribute__ method. Model values attribute is a class attribute, which means it related to class but not to an instance of a class. Every Model instance uses the same values.

To solve your problem you should move values from class to instance level:

class Model:
    schema = [
        'foo',
        'bar'
    ]

    def __init__(self, input_values=None):
        self.values = {}
        if input_values is not None:
            self.validate_input(input_values)

    def __call__(self, *args, **kwargs):
        if args[0] is not None:
            self.validate_input(args[0])

    def __iter__(self):
        for key, value in self.values.items():
            yield (key, value)

    def __getattribute__(self, item):
        if item in object.__getattribute__(self, 'values'):
            return object.__getattribute__(self, 'values')[item]
        return object.__getattribute__(self, item)

    def validate_input(self, input_values):
        for field, value in input_values.items():
            if field in self.schema:
                self.values[field] = value

For more information about class and instance variables follow link.

Yurii Karabas
  • 543
  • 4
  • 10