0

I have a somewhat complex data structure: a default ordered dictionary of ordered dictionaries, following what's coded here. The class works fine, as I'm able to create ordered dictionaries by default if a key is missing. I would like to further extend the capability of this class, and override the __iter__ method so that I can iterate only over the ordered dictionary of certain key. That is, if I have

import sys
import numpy as np
from collections import OrderedDict


class OrderedDefaultdict(OrderedDict):
    def __init__(self, *args, **kwargs):

        self.d = None
        if not args:
            self.default_factory = None
        else:
            if not (args[0] is None or callable(args[0])):
                raise TypeError('first argument must be callable or None')
            self.default_factory = args[0]
            args = args[1:]
        super(OrderedDefaultdict, self).__init__(*args, **kwargs)

    def __iter__(self):
        for k in self[self.d].values():
            yield k

    def __call__(self, key):
        self.d = key
        return self

    def __missing__ (self, key):

        self.d = max(self.d, key) if self.d else key
        if self.default_factory is None:
            raise KeyError(key)
        self[key] = default = self.default_factory()
        return default

    def __reduce__(self):  # optional, for pickle support
        args = (self.default_factory,) if self.default_factory else ()
        return self.__class__, args, None, None, self.iteritems()

Tree = lambda: OrderedDefaultdict(Tree)

You see that the code saves the maximum key in a variable self.d, and I would like to iterate over its corresponding dictionary by default.

def main(argv=()):

    tree = Tree()

    tree[0]['n1'] = np.array([[3]])
    tree[1]['l2'] = np.array([[ 0,  4],
                            [ 4,  5],
                            [40, 41],
                            [41, 42],
                            [57,  3],
                            [57,  3]])
    tree[2]['t3x'] = np.array([[188, 401, 400],
                           [188, 187, 401],
                           [187, 205, 401],
                           [324, 306, 417],
                           [306, 305, 417],
                           [305, 416, 417]])
    tree[2]['q3'] = np.array([[188, 401, 400, 0],
                           [188, 187, 401, 0],
                           [187, 205, 401, 0],
                           [323, 324, 417, 0],
                           [324, 306, 417, 0],
                           [306, 305, 417, 0],
                           [305, 416, 417, 0]])

    for el in tree:
        print(el)

    return 0

if __name__ == "__main__":
    sys.exit(main())

In the code above, I try to iterate over the dictionary of the highest dimension. But this code doesn't work because I get into an infinite recursion in the __iter__ method and I can't understand why. I've been following advice on how to implement this function here.

Community
  • 1
  • 1
aaragon
  • 2,314
  • 4
  • 26
  • 60
  • In the end of the first code block: `Tree = lambda: OrderedDefaultdict(Tree)` – aaragon Dec 21 '15 at 11:24
  • 1
    Do you mean `for k in OrderedDict.__iter__(self[self.d]): yield self[self.d][k]` – Padraic Cunningham Dec 21 '15 at 12:33
  • Yes! that worked, but why? – aaragon Dec 21 '15 at 12:35
  • Having serious touchpad issues here, it is essentially the difference between overriding something like getitem with `super(OrderedDefaultdict, self).__getitem__(key)` vs `return self[self.d]` http://stackoverflow.com/questions/19861024/how-can-reload-getitem-so-that-when-key-doesnt-exist-can-create-it – Padraic Cunningham Dec 21 '15 at 12:43
  • With `for k in self[self.d]` you are calling `self. __iter__` so you end up with an infinite recursion – Padraic Cunningham Dec 21 '15 at 13:40
  • I get it, so you're basically calling `__iter__` on the super class? I thought the syntax was different for this. – aaragon Dec 21 '15 at 13:43
  • Yes, `dict.__iter__` would also work, you just cannot iterate over self or self.values etc.. as they all call __iter__ so you just keep looping infinitely. With `OrderedDict.__iter__(self[self.d]):` you are calling `OrderedDict.__iter__` not self.__iter__, you cannot add .values as again you would be calling self.__iter__ – Padraic Cunningham Dec 21 '15 at 14:05
  • If you prefer you can use `for k in super(OrderedDefaultdict, self[self.d]).__iter__():` will also work – Padraic Cunningham Dec 21 '15 at 14:11

0 Answers0