我需要一个存储字典的字典数据结构,如下所示:
custom = {1: {'a': np.zeros(10), 'b': np.zeros(100)},
2: {'c': np.zeros(20), 'd': np.zeros(200)}}
但问题是我在代码中多次迭代这个数据结构。每次迭代它时,我都需要遵守迭代的顺序,因为这个复杂数据结构中的所有元素都映射到一维数组(如果你愿意,可以序列化),因此顺序很重要。我考虑过写一个有序dict
的有序dict
,但我不确定这是否是正确的解决方案,因为我似乎选择了错误的数据结构。对我的案子来说,最合适的解决办法是什么?
更新
所以这就是我到目前为止所提出的:
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.iterator = iter(self[-1].keys())
self.etype = next(self.iterator)
self.idx = 0
def __iter__(self):
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 = -1 - d
self.iterator = iter(self[self.d].keys())
self.etype = next(self.iterator)
self.idx = 0
return self
def main(argv=()):
tst = Test(elements)
for el in tst:
print(el)
# loop over a lower dimension
for el in tst(-2):
print(el)
print(tst)
return 0
if __name__ == "__main__":
sys.exit(main())
我可以在这个有序的结构中迭代多次,我实现了__call__
,所以我可以遍历较低的维度。我不喜欢这样的事实:如果列表中没有较低的维度,它不会给我任何错误。我也有这种感觉,每次调用return self[self.d][self.etype][self.idx-1]
都不如字典上的原始迭代效率低。这是真的?我怎样才能改善这个?
答案 0 :(得分:2)
我认为使用OrderedDict
是最好的方法。它们是内置且相对较快的:
custom = OrderedDict([(1, OrderedDict([('a', np.zeros(10)),
('b', np.zeros(100))])),
(2, OrderedDict([('c', np.zeros(20)),
('d', np.zeros(200))]))])
如果您想轻松迭代数据结构的内容,您可以随时提供实用程序功能:
def iter_over_contents(data_structure):
for delem in data_structure.values():
for v in delem.values():
for row in v:
yield row
请注意,在允许yield from <expression>
的Python 3.3+中,可以消除最后一个for
循环:
def iter_over_contents(data_structure):
for delem in data_structure.values():
for v in delem.values():
yield from v
有了其中一个,你就可以写下这样的东西:
for elem in iter_over_contents(custom):
print(elem)
并隐藏复杂性。
虽然可以定义您自己的类,以尝试封装此数据结构并使用类似iter_over_contents()
生成器函数的方法作为其__iter__()
方法,但这种方法很可能更慢,不会允许使用两级索引的表达式,如下所示:
custom[1]['b']
使用嵌套字典(或OrderedDefaultdict
s,如我的其他答案所示)。
答案 1 :(得分:2)
这是另一种使用OrderedDefaultdict
定义所需树状数据结构的替代方法。我正在重复使用我的另一个answer中的定义。
要使用它,您必须确保在以后按照您想要访问它们的顺序定义条目。
class OrderedDefaultdict(OrderedDict):
def __init__(self, *args, **kwargs):
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 __missing__ (self, 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)
custom = Tree()
custom[1]['a'] = np.zeros(10)
custom[1]['b'] = np.zeros(100)
custom[2]['c'] = np.zeros(20)
custom[2]['d'] = np.zeros(200)
我不确定我理解你的后续问题。如果数据结构限制为两个级别,则可以使用嵌套的for
循环按其定义的顺序迭代其元素。例如:
for key1, subtree in custom.items():
for key2, elem in subtree.items():
print('custom[{!r}][{!r}]: {}'.format(key1, key2, elem))
(在Python 2中,您希望使用iteritems()
代替items()
。)
答案 2 :(得分:1)
你能用一个字典列表吗?
custom = [{'a': np.zeros(10), 'b': np.zeros(100)},
{'c': np.zeros(20), 'd': np.zeros(200)}]
如果外部字典是您按正确顺序需要的唯一字典,则可以使用。您仍然可以使用custom[0]
或custom[1]
访问内部词典(请注意,索引现在从0
开始)。
如果未使用所有索引,则可以执行以下操作:
custom = [None] * maxLength # maximum dict size you expect
custom[1] = {'a': np.zeros(10), 'b': np.zeros(100)}
custom[2] = {'c': np.zeros(20), 'd': np.zeros(200)}
答案 3 :(得分:0)
您可以在重复排序时修复键的顺序:
for key in sorted(custom.keys()):
print(key, custom[key])
如果要减少sorted()
- 调用,您可能希望将密钥存储在一个额外的列表中,该列表将作为您的迭代顺序:
ordered_keys = sorted(custom.keys())
for key in ordered_keys:
print(key, custom[key])
您应该准备好根据需要对数据结构进行尽可能多的迭代。