How can this function be rewritten to implement OrderedDict?
我有下面的函数,它完成了将XML文件解析为字典的简单工作。
不幸的是,由于python字典没有排序,所以我无法按自己的意愿循环浏览节点。
如何更改此值,以便它输出一个有序字典,该字典在循环使用"for"时反映节点的原始顺序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | def simplexml_load_file(file): import collections from lxml import etree tree = etree.parse(file) root = tree.getroot() def xml_to_item(el): item = None if el.text: item = el.text child_dicts = collections.defaultdict(list) for child in el.getchildren(): child_dicts[child.tag].append(xml_to_item(child)) return dict(child_dicts) or item def xml_to_dict(el): return {el.tag: xml_to_item(el)} return xml_to_dict(root) x = simplexml_load_file('routines/test.xml') print x for y in x['root']: print y |
输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 | {'root': { 'a': ['1'], 'aa': [{'b': [{'c': ['2']}, '2']}], 'aaaa': [{'bb': ['4']}], 'aaa': ['3'], 'aaaaa': ['5'] }} a aa aaaa aaa aaaaa |
号
如何实现collections.ordereddict,以确保获得正确的节点顺序?
用于引用的XML文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <root> 1 <c>2</c> 2 </aa> 3</aaa> <bb>4</bb> </aaaa> 5</aaaaa> </root> |
您可以使用新的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | import collections class OrderedDefaultdict(collections.OrderedDict): """ A defaultdict with OrderedDict as its base class.""" def __init__(self, default_factory=None, *args, **kwargs): if not (default_factory is None or isinstance(default_factory, collections.Callable)): raise TypeError('first argument must be callable or None') super(OrderedDefaultdict, self).__init__(*args, **kwargs) self.default_factory = default_factory # called by __missing__() def __missing__(self, key): if self.default_factory is None: raise KeyError(key,) self[key] = value = self.default_factory() return value def __reduce__(self): # optional, for pickle support args = (self.default_factory,) if self.default_factory else tuple() return self.__class__, args, None, None, self.iteritems() def __repr__(self): # optional return '%s(%r, %r)' % (self.__class__.__name__, self.default_factory, list(self.iteritems())) def simplexml_load_file(file): from lxml import etree tree = etree.parse(file) root = tree.getroot() def xml_to_item(el): item = el.text or None child_dicts = OrderedDefaultdict(list) for child in el.getchildren(): child_dicts[child.tag].append(xml_to_item(child)) return collections.OrderedDict(child_dicts) or item def xml_to_dict(el): return {el.tag: xml_to_item(el)} return xml_to_dict(root) x = simplexml_load_file('routines/test.xml') print(x) for y in x['root']: print(y) |
从测试XML文件生成的输出如下所示:
输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | {'root': OrderedDict( [('a', ['1']), ('aa', [OrderedDict([('b', [OrderedDict([('c', ['2'])]), '2'])])]), ('aaa', ['3']), ('aaaa', [OrderedDict([('bb', ['4'])])]), ('aaaaa', ['5']) ] ) } a aa aaa aaaa aaaaa |
号
我觉得这和你想要的差不多。
*如果您的python版本没有ordereddict(在2.5版中介绍),那么您可以使用Raymond Hettinger的ordered dictionary for py2.4 activestate recipe作为基类。
次要更新:
添加了一个
Martineau的配方对我有用,但它与从defaultdict继承的copy()方法有问题。以下方法解决了这个缺点:
1 2 3 4 5 | class OrderedDefaultDict(OrderedDict): #Implementation as suggested by martineau def copy(self): return type(self)(self.default_factory, self) |
请考虑,此实现没有deepcopy,这在大多数情况下似乎特别适合于默认字典,而不是正确的做法。
这里的答案中列出了许多可能实现ordereddict的方法:如何按照插入顺序从字典中检索条目?
通过复制其中一个实现,您可以创建自己的ordereddict模块以在自己的代码中使用。我假设由于运行的是python版本,您没有访问ordereddict的权限。
问题的一个有趣方面是可能需要默认dict功能。如果需要,可以实现