1

With the following class

from collections import defaultdict, OrderedDict
class NestedOrderedDict(OrderedDict):
    def __missing__(self,k):
        val = self[k] = NestedOrderedDict()
        return self[k]

I tried to make ordered dictionary of dictionary of list

orddodol = NestedOrderedDict()
orddodol["foo"]["a"].append(1)
orddodol["foo"]["b"].append(1)
orddodol["foo"]["b"].append(12)
orddodol["bar"]["a"].append(2)
orddodol["bar"]["a"].append(3)

But it failed giving

AttributeError: 'NestedOrderedDict' object has no attribute 'append'

What's the right way to do it?

pdubois
  • 7,640
  • 21
  • 70
  • 99

3 Answers3

4

You have to initialize the second level dictionary values as lists:

from collections import defaultdict, OrderedDict
class NestedOrderedDict(OrderedDict):
    def __missing__(self,k):
        val = self[k] = NestedOrderedDict()
        return self[k]

orddodol = NestedOrderedDict()
orddodol["foo"]["a"] = []           # these
orddodol["foo"]["b"] = []           # three
orddodol["bar"]["a"] = []           # lines
orddodol["foo"]["a"].append(1)
orddodol["foo"]["b"].append(1)
orddodol["foo"]["b"].append(12)
orddodol["bar"]["a"].append(2)
orddodol["bar"]["a"].append(3)
jedwards
  • 29,432
  • 3
  • 65
  • 92
4

You almost did it right :) If you want to create ordered dictionary of dictionary of list, then you'd better use defaultdict(list) for that purpose:

from collections import defaultdict, OrderedDict
class NestedOrderedDict(OrderedDict):
    def __missing__(self, k):
   >>>> self[k] = defaultdict(list)
        return self[k]

orddodol = NestedOrderedDict()
orddodol["foo"]["a"].append(1)
orddodol["foo"]["b"].append(1)
orddodol["foo"]["b"].append(12)
orddodol["bar"]["a"].append(2)
orddodol["bar"]["a"].append(3)
Artem Mezhenin
  • 5,539
  • 6
  • 32
  • 51
  • Bah just beat me to it :) – Paul Rooney Mar 26 '15 at 03:46
  • This completely changes what `NestedOrderedDict` does -- you can no longer do something like `orddodol["aa"]["bb"]["cc"] = []` for example. – jedwards Mar 26 '15 at 03:53
  • yes, but question was about ordered dictionary of dictionary of list (just two layers) – Artem Mezhenin Mar 26 '15 at 03:55
  • Given that the code has keys like "foo" and "bar", I considered it an example and didn't try to assume anything about it only working for two layers. He asked about the "right way" to do it, which, while this works for his example, it clearly is not. – jedwards Mar 26 '15 at 03:56
1
from collections import defaultdict, OrderedDict
class NestedOrderedDict(OrderedDict):
    def __missing__(self,k):
        val = self[k] = NestedOrderedDict()
        return self[k]

orddodol = NestedOrderedDict()
orddodol["foo"]["a"] = []
orddodol["foo"]["b"] = []
orddodol["bar"]["a"] = []
orddodol["foo"]["a"].append(1)
orddodol["foo"]["b"].append(1)
orddodol["foo"]["b"].append(12)
orddodol["bar"]["a"].append(2)
orddodol["bar"]["a"].append(3)

or you can implement a defaultdict with ordered like this post Can I do an ordered, default dict in Python

from collections import OrderedDict, Callable
class DefaultOrderedDict(OrderedDict):
    # Source: https://stackoverflow.com/a/6190500/562769
    def __init__(self, default_factory=None, *a, **kw):
        if (default_factory is not None and
           not isinstance(default_factory, Callable)):
            raise TypeError('first argument must be callable')
        OrderedDict.__init__(self, *a, **kw)
        self.default_factory = default_factory

    def __getitem__(self, key):
        try:
            return OrderedDict.__getitem__(self, key)
        except KeyError:
            return self.__missing__(key)

    def __missing__(self, key):
        if self.default_factory is None:
            raise KeyError(key)
        self[key] = value = self.default_factory()
        return value

    def __reduce__(self):
        if self.default_factory is None:
            args = tuple()
        else:
            args = self.default_factory,
        return type(self), args, None, None, self.items()

    def copy(self):
        return self.__copy__()

    def __copy__(self):
        return type(self)(self.default_factory, self)

    def __deepcopy__(self, memo):
        import copy
        return type(self)(self.default_factory,
                          copy.deepcopy(self.items()))

    def __repr__(self):
        return 'OrderedDefaultDict(%s, %s)' % (self.default_factory,
                                               OrderedDict.__repr__(self))
Community
  • 1
  • 1
Tong Liu
  • 46
  • 2