【问题标题】:How can this function be rewritten to implement OrderedDict? [duplicate]如何重写这个函数来实现 OrderedDict? [复制]
【发布时间】:2011-05-06 19:06:23
【问题描述】:

我有以下函数,它可以将 XML 文件解析为字典。

不幸的是,由于 Python 字典没有排序,我无法随意循环浏览节点。

如何更改它,以便在使用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>

【问题讨论】:

    标签: python xml collections lxml


    【解决方案1】:

    这里的答案中列出了许多可能的 OrderedDict 实现:How do you retrieve items from a dictionary in the order that they're inserted?

    您可以通过复制其中一个实现来创建自己的 OrderedDict 模块,以便在您自己的代码中使用。我假设由于您正在运行的 Python 版本,您无权访问 OrderedDict。

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

    【讨论】:

      【解决方案2】:

      您可以使用新的 OrderedDictdict 子类,该子类已在 2.7 版中添加到标准库的 collections 模块中。实际上,您需要的是一个不存在的 Ordered+defaultdict 组合 — 但可以通过子类化 OrderedDict 来创建一个,如下所示:

      如果您的 Python 版本没有 OrderedDict,您应该可以使用 Raymond Hettinger 的 Ordered Dictionary for Py2.4 ActiveState 配方作为基类。

      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 callable(default_factory)):
                  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, iter(self.items())
      
          def __repr__(self):  # Optional.
              return '%s(%r, %r)' % (self.__class__.__name__, self.default_factory, self.items())
      
      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 文件生成的输出如下所示:

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

      我认为这与您想要的很接近。

      小更新:

      添加了一个__reduce__() 方法,该方法将允许正确地腌制和取消腌制类的实例。这不是这个问题所必需的,但出现在similar one

      【讨论】:

      【解决方案3】:

      martineau 的配方对我有用,但是从 DefaultDict 继承的方法 copy() 存在问题。以下方法解决了这个缺点:

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

      请考虑,此实现不进行深度复制,这似乎特别适用于默认字典,而不是在大多数情况下正确的做法

      【讨论】:

        猜你喜欢
        • 2015-06-17
        • 1970-01-01
        • 2021-06-24
        • 2011-02-26
        • 2010-12-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多