Thursday, August 8, 2013

Python - Putting dot in the dict

One good thing about JavaScript is that one can access its dictionaries by dot notation. Well, JavaScript does not have dictionaries and people use object instead, but the point is that I've wanted to have data structure in Python that would allow me this:
>>> d = MagicDict({1: {2: 3}})
>>> d.a.b.c.d = "magic!"
>>> import json; print json.dumps(d, indent=2)
{
  "a": {
    "b": {
      "c": {
        "d": "magic!"
      }
    }
  }, 
  "1": {
    "2": 3
  }
}
>>>

Actually this small experiment inspired by DotDict found in pyes project.

So to make myself happy, I've bred DotDict and defaultdict to complete the picture:

from collections import defaultdict

class DefaultDotDict(defaultdict):
    def __init__(self, *args, **kwargs):
        super(DefaultDotDict, self).__init__(DefaultDotDict)
        if args or kwargs:
            dict.__init__(self, *args, **kwargs)

    def __getattr__(self, attr):
        if attr.startswith('_'):
            raise AttributeError  # Magic only works for key that do not start with "_"
        val = self.__getitem__(attr)
        if isinstance(val, dict) and not isinstance(val, DefaultDotDict):
            val = DefaultDotDict(val)
        self[attr] = val
        return val

    def __setattr__(self, attr, val):
        return self.__setitem__(attr, val)

    def __delattr__(self, attr):
        return self.__delitem__(attr)

    def __deepcopy__(self, memo):
        return DefaultDotDict(
                 [(copy.deepcopy(k, memo), copy.deepcopy(v, memo)) for k, v in self.items()])