I am facing the following problem: I have a special data structure that is a dictionary whose keys are integers (dimensions). The values are also dictionaries whose keys are strings (geometric types) and whose values are numpy arrays (connectivities). Something like this:
custom = {1: {'a': np.zeros(10), 'b': np.zeros(100)}, 2:{'c': np.zeros(20), 'd': np.zeros(200)}}
I use this data structure quite a lot in the code, and every time I need to iterate over all rows in the numpy arrays of this data structure (all values of the dictionaries), I have to type:
for d, delem in custom.items():
for k, v in delem.items():
for row in v:
print(row)
And this is way too verbose. So to make things easier to other developers, I am trying to encapsulate this behavior in a custom class so that I can type:
for row in custom:
print(row)
Also, an important requirement is that all rows have to be traversed "in order".
And so I came up with a Test
class that derives from list
. The class is initialized by passing a dictionary (maybe I can improve this later by just adding the elements directly to it). I have overridden the __iter__
and __next__
methods to provide the iteration type I want. By default, iteration is done through the highest dimension present. But the user can do iteration over lower-dimensional dictionaries and for that I overrode the __call__
method.
The technique works, I an iterate as many times as I want over this data structure, and I accomplished the simplicity I wanted. But I'm a bit concerned about performance, as I don't think that calling self[self.d][self.etype][self.idx-1]
is very efficient. Maybe there are other improvements to be made as well that I don't see.
import sys
import numpy as np
from collections import OrderedDict
custom = {0: {'n1': np.array([[3]])}, 1: {'l2': np.array([[ 0, 4],
[ 4, 5],
[40, 41],
[41, 42], [57, 3],
[57, 3]])}, 2: {'t3x': np.array([[188, 401, 400],
[188, 187, 401],
[187, 205, 401],
[324, 306, 417],
[306, 305, 417],
[305, 416, 417]]), '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]])}}
class Test(list):
def __init__(self, *args, **kwargs):
super(Test, self).__init__(*args, **kwargs)
for k,v in args[0].items():
self[k] = OrderedDict(v)
self.d = -1
self.setup()
def setup(self):
self.iterator = iter(self[self.d].keys())
self.etype = next(self.iterator)
self.idx = 0
def __iter__(self):
self.setup()
return self
def __next__(self):
try:
self.idx += 1
return self[self.d][self.etype][self.idx-1]
except IndexError:
self.etype = next(self.iterator)
self.idx = 0
return self[self.d][self.etype][self.idx-1]
def __call__(self, d):
self.d = d-1
return self
def main(argv=()):
tst = Test(custom)
print(tst)
# iterate over the container
for el in tst:
print(el)
# iterate again over the container
for el in tst:
print(el)
# iterate over lower dimension
for el in tst(-1):
print(el)
return 0
if __name__ == "__main__":
sys.exit(main())