Welcome, guest | Sign In | My Account | Store | Cart
3

An example of overloading __setattr__ and __getattr__ in classes. This example maps attribute names to dicitonary members.

Python, 42 lines
 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.

3 comments

Gigi Sayfan 7 years, 2 months ago  # | flag

__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.

Greg 4 years, 4 months ago  # | flag

In the __setattr__ method, this line is never True:

elif self.__dict__.has_key(item):

Replacing it with the following makes it better:

elif item in self:
Greg 1 year, 9 months ago  # | flag

Please ignore my previous comment!

(The line is checking for attributes added before self.__initialised is set True)

Add a comment

Sign in to comment

Created by Michael Foord on Tue, 1 Mar 2005 (PSF)
Python recipes (4293)
Michael Foord's recipes (20)

Required Modules

  • (none specified)

Other Information and Tasks