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

Dictionary object that can also be accessed via attributes

Python, 44 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
43
44
import sys
from collections import MutableMapping

class AttrDict(MutableMapping):

    """Dict-like object that can be accessed by attributes

    >>> obj = AttrDict()
    >>> obj['test'] = 'hi'
    >>> print obj.test
    hi
    >>> del obj.test
    >>> obj.test = 'bye'
    >>> print obj['test']
    bye
    >>> print len(obj)
    1
    >>> obj.clear()
    >>> print len(obj)
    0
    """

    def __init__(self, *args, **kwargs):
        self.__dict__.update(*args, **kwargs)

    def __getitem__(self, key):
        return self.__getattribute__(key)

    def __setitem__(self, key, val):
        self.__setattr__(key, val)

    def __delitem__(self, key):
        self.__delattr__(key)

    def __iter__(self):
        return iter(self.__dict__)

    def __len__(self):
        return len(self.__dict__)


if __name__ == '__main__':
    import doctest
    doctest.testmod()

This container object allows both key and attribute access while implementing all of the dict methods.

5 comments

Jeremy Sandell 14 years, 3 months ago  # | flag

This appears to do the same thing - at the very least, it passes the doctest - with less boilerplate.

class AttrDict(dict):
    def __init__(self, d = {}):
        dict.__init__(self, d)
        self.__dict__ = self
Ryan Smith-Roberts 12 years, 10 months ago  # | flag

For a small performance hit, one can expose the full dict interface:

class AttrDict(dict):
    def __init__(self, *a, *kw):
        dict.__init__(self, *a, *kw)
        self.__dict__ = self
Ryan Smith-Roberts 12 years, 10 months ago  # | flag

I suppose if I actually test the code, I might have fewer bugs:

class AttrDict(dict):
    def __init__(self, *a, **kw):
        dict.__init__(self, *a, **kw)
        self.__dict__ = self
Martin Miller 11 years, 2 months ago  # | flag

I think @Ryan has the right idea about the better way to do something like this, but feel a more "canonical" implementation would be this way:

class AttrDict(dict):
    def __init__(self, *args, **kwargs):
        super(AttrDict, self).__init__(*args, **kwargs)
        self.__dict__ = self

I've also seen this recipe called JSObject because it creates Python objects that act like Javascript objects.

Martin Miller 9 years, 4 months ago  # | flag

For anyone interested, a similar package is now available from PyPI.