__Getattr__ part counter

I am trying to find a way to set the values dict

encapsulated in class

, for example using __getattr__

I can return an internal value dict

, however it __setattr__

gets called even when the attributes exist, which makes my implementation ugly. The example below is simplified, my actual class inherits from Subject

class

(subject part of observer template)

I am trying to achieve something like this:

obj = Example()
obj.username = 'spidername' # all OK username is a key in the internal dict
# but company is not a key in the internal dict so
obj.company = 'ABC' # will raise AttributeError

      

and I am asking if there is a better way than how I am doing below:

class Example(object):
    def __init__(self, table=None):
        self._fields = {}
        self._table = table

    def _set_fields(self):
        """
        this method will be implemented by 
        subclasses and used to set fields names and values
        i.e.
        self._field['username'] = Field(default='unknown', is_primary=False)
        """
        raise NotImplementedError

    def __getattr__(self, name):
        """
        great this method is only called when "name"
        is not an attribute of this class
        """
        if name in self._fields:
            return self._fields[name].value
        return None

    def __setattr__(self, name, value):
        """
        not so great, this method is called even for
        attributes that exists in this class

        is there a better way to do the following?             
        this can be in __init__, but its still ugly
        """
        attribs = ['_fields', '_table'] 
        if name in attribs:
            super(Example, self).__setattr__(name, value)
        else:
            if name in self._fields:
                self._fields[name].value = value
            else:
                raise AttributeError

      

EDIT: Corrected comment in code, missin quotes added

+3


source to share


2 answers


The problem is that the attributes don't exist when they are first assigned. In __init__

when you first assign the dict _fields

is _fields

not an attribute. It becomes a pre-existing attribute after it is assigned. You can use __slots__

it if you know in advance what attributes are, but I assume you don't. So my suggestion would be to insert them into the dict instance manually:

class Example(object):
    def __init__(self, table=None):
        self.__dict__['_fields'] = {}
        self.__dict__['_table'] = table

    ...

    def __setattr__(self, name, value):
        if name in self._fields:
            self._fields[name].value = value
        else:
            raise AttributeError

      



However, with this implementation, the only way to add or change instance attributes later is through __dict__

. But I guess this is unlikely.

+3


source


FWIW, your overall goal can be achieved directly using __ slots__ :



>>> class Example(object):
        __slots__ = ['username']

>>> obj = Example()
>>> obj.username = 'spiderman'
>>> obj.company = 'ABC'

Traceback (most recent call last):
  File "<pyshell#18>", line 1, in <module>
    obj.company = 'ABC'
AttributeError: 'Example' object has no attribute 'company'

      

+2


source







All Articles