如何重写这个函数来实现OrderedDict?

我有以下function,它将XML文件parsing为字典粗略的工作。

不幸的是,由于Python字典没有sorting,所以我无法按照我的意愿循环访问节点。

我该如何改变它,所以它输出一个有序的字典,它反映了当用'for'循环时节点的原始顺序。

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 

输出:

 {'root': { 'a': ['1'], 'aa': [{'b': [{'c': ['2']}, '2']}], 'aaaa': [{'bb': ['4']}], 'aaa': ['3'], 'aaaaa': ['5'] }} a aa aaaa aaa aaaaa 

我怎样才能实现collections.OrderedDict,以便我可以肯定获得正确的节点顺序?

XML文件供参考:

 <root> <a>1</a> <aa> <b> <c>2</c> </b> <b>2</b> </aa> <aaa>3</aaa> <aaaa> <bb>4</bb> </aaaa> <aaaaa>5</aaaaa> </root> 

您可以使用已添加到版本2.7 *中的标准库collections模块的OrderedDict dict子类。 实际上你需要的是Ordered + defaultdict组合,它不存在 – 但是可以通过子类OrderedDict创build一个,如下所示:

 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) 

从testingXML文件生成的输出如下所示:

输出:

 {'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(在v2.5中引入),则可以使用Raymond Hettinger的Ordered Dictionary for Py2.4 ActiveState recipe作为基类。

次要更新:

增加了一个__reduce__()方法,该方法将允许该类的实例进行酸洗和取消。 这个问题没有必要,但是出现了类似的问题。

OrderedDict有许多可能的实现在这里的答案中列出: 你如何检索一个字典中的项目,他们插入的顺序?

您可以通过复制其中一个实现来创build自己的OrderedDict模块,以便在自己的代码中使用。 由于您正在运行的是Python版本,因此我认为您无法访问OrderedDict。

您的问题的一个有趣的方面是可能需要defaultdictfunction。 如果你需要这个,你可以实现__missing__方法来获得所需的效果。

martineau的配方适用于我,但是从DefaultDictinheritance的copy()方法有问题。 以下方法解决了这个缺点:

 class OrderedDefaultDict(OrderedDict): #Implementation as suggested by martineau def copy(self): return type(self)(self.default_factory, self) 

请考虑一下,这个实现没有深度拷贝,这对于默认字典来说似乎尤其是在大多数情况下是正确的