An example of overloading __setattr__ and __getattr__ in classes. This example maps attribute names to dicitonary members.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | class attrExample(dict):
"""Example of overloading __getatr__ and __setattr__
This example creates a dictionary where members can be accessed as attributes
"""
def __init__(self, indict=None, attribute=None):
if indict is None:
indict = {}
# set any attributes here - before initialisation
# these remain as normal attributes
self.attribute = attribute
dict.__init__(self, indict)
self.__initialised = True
# after initialisation, setting attributes is the same as setting an item
def __getattr__(self, item):
"""Maps values to attributes.
Only called if there *isn't* an attribute with this name
"""
try:
return self.__getitem__(item)
except KeyError:
raise AttributeError(item)
def __setattr__(self, item, value):
"""Maps attributes to values.
Only if we are initialised
"""
if not self.__dict__.has_key('_attrExample__initialised'): # this test allows attributes to be set in the __init__ method
return dict.__setattr__(self, item, value)
elif self.__dict__.has_key(item): # any normal attributes are handled normally
dict.__setattr__(self, item, value)
else:
self.__setitem__(item, value)
if __name__ == '__main__':
example = attrExample(attribute='fish')
print example.attribute
example.fish = 'test'
print example['fish']
print example.fish
|
Overloading __setattr__ and __getattr__ in classes can be a useful way of custmizing attribute access. For example transforming attributes or function arguments as they are called and set.
__getattr__ and __setattr__ don't behave symetrically and you have to take slight care in their use. For example overloading __setattr__ can make it difficult to set normal attributes in the __init__ method of an object.
This example is a subclass of dict - but maps attribtues to dictionary members. It allows you to set normal attributes in the __init__ method. After that setting attributes creates a new dictionary memebr (rather than an attribute).
Fetching an attribute first checks for normal attribtues, if the attribute asked for isn't present it checks for dictionary members.
__getattribute__ hook. There is also the __getattribute__() hook, which is called (if defined) for both exisitng and non-exisitng attributes. If __getattribute__() is called and doesn't raise AttributeError then __getattr__() is not called. If __getattribute__() raises AttributeError then __getattr__ gets another change to save the day.
In the __setattr__ method, this line is never True:
Replacing it with the following makes it better:
Please ignore my previous comment!
(The line is checking for attributes added before self.__initialised is set True)