Biboroku

Attribute Access with Dict

Written by Taro Sato, on . Tagged: Python

Python dict is useful. The access to a nested item can be tedious, however. For example,

data = {
  "hosts": {
    "name": "localhost",
    "cidr": "127.0.0.1/8",
  }
}

Here, data["hosts"]["cidir"] would get you "127.0.0.1/8", but all those quotes and brackets can be annoying to type and read. A singly nested dict like the case above is not bad, but this can quickly become tedium as the nesting deepens. It would be so much more succinct to be able to access an item at a key through attributes, as in data.hosts.cidir.

Such attribute access is fairly simple to implement using the __dict__ magic attribute Python uses to define object attributes:

from collections import UserDict

class AttrDict(UserDict):
    pass

def attrdict(d: dict) -> AttrDict:
    def addattrs(d):
        if not isinstance(d, dict):
            return d
        obj = AttrDict()
        for k in d:
            obj[k] = obj.__dict__[k] = addattrs(d[k])
        return obj

    obj = AttrDict()
    obj.update(d)
    obj.__dict__.update(addattrs(d))
    return obj

There are caveats. If a key in the input dict contains invalid characters for an attribute name (e.g., space), the item for the key will not be accessible as an attribute (though it remains accessible through an index). Keys also need to be str, whereas dict keys in general can be any hashable Python object. More importantly, if any key in the input dict conflicts with the method or attribute names of the dict interface, the object may not function properly as dict anymore.

So long as the limitations are understood, a solution like this can be good enough in some cases, simple JSON objects with well-defined structure, for example.