__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
source to share
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.
source to share
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'
source to share