【问题标题】:how to convert a nested OrderedDict to dict?如何将嵌套的 OrderedDict 转换为 dict?
【发布时间】:2014-09-23 02:37:03
【问题描述】:

我有一个嵌套的OrderedDict 我想转换为dict。在其上应用dict() 显然只会转换最后一个条目的最外层。

from collections import OrderedDict

od = OrderedDict(
    [
        (u'name', u'Alice'),
        (u'ID', OrderedDict(
            [
                (u'type', u'card'),
                (u'nr', u'123')
            ]
        )),
        (u'name', u'Bob'),
        (u'ID', OrderedDict(
            [
                (u'type', u'passport'),
                (u'nr', u'567')
            ]
        ))
    ]
)

print(dict(od))

输出:

{u'name': u'Bob', u'ID': OrderedDict([(u'type', u'passport'), (u'nr', u'567')])}

有没有直接转换所有出现的方法?

【问题讨论】:

  • 您只想转换OrderedDict 实例吗?
  • 为什么要转换它?您几乎可以在dict 工作的任何地方使用OrderedDict
  • @PatrickCollins:对不起,我不明白你的问题。对于所有元素,我希望将所有 OrderedDicts 转换为 dicts(我在几秒钟前的问题中澄清了这一点)
  • @jonrsharpe:我会得到一个巨大的 OrderedDict(几百兆到几场演出),我读到内存开销很大(大约两倍)。由于我不需要订单,因此我至少会对其进行调整以使其易于管理。

标签: python dictionary


【解决方案1】:

此代码应适用于嵌套列表。

def nested_convert_to_dict(input: [dict, collections.OrderedDict]):
    if isinstance(input, collections.OrderedDict):
        res = dict(input)
    else:
        res = input
    try:
        for key, value in res.items():
            res[key] = nested_convert_to_dict(value)
            if isinstance(value, list):
                new_value = []
                for item in value:
                    if isinstance(item, collections.OrderedDict):
                        item = nested_convert_to_dict(item)
                    new_value.append(item)
                res[key] = new_value
    except AttributeError:
        pass
    return res

【讨论】:

    【解决方案2】:

    这是一个同时处理列表和元组的版本。在这个comment 中,OP 提到字典列表也是一个需要处理的案例。

    注意,这也将元组转换为列表。保留元组作为练习留给读者:)

    def od2d(val):                                                                  
      if isinstance(val, (OrderedDict, dict)):                                    
          return {k: od2d(v) for k, v in val.items()}                             
      elif isinstance(val, (tuple, list)):                                        
          return [od2d(v) for v in val]                                           
      else:                                                                       
          return val 
    

    【讨论】:

      【解决方案3】:

      最简单的解决方案是使用 json 转储和加载

      from json import loads, dumps
      from collections import OrderedDict
      
      def to_dict(input_ordered_dict):
          return loads(dumps(input_ordered_dict))
      

      注意:以上代码适用于 json 已知为可序列化对象的字典。默认对象类型列表可以找到here

      所以,如果有序字典不包含特殊值,这就足够了。

      编辑:基于 cmets,让我们改进上面的代码。让我们说,input_ordered_dict 可能包含默认情况下无法由 json 序列化的自定义类对象。 在这种情况下,我们应该将json.dumpsdefault 参数与我们的自定义序列化程序一起使用。

      (例如):

      from collections import OrderedDict as odict
      from json import loads, dumps
      
      class Name(object):
          def __init__(self, name):
              name = name.split(" ", 1)
              self.first_name = name[0]
              self.last_name = name[-1]
      
      a = odict()
      a["thiru"] = Name("Mr Thiru")
      a["wife"] = Name("Mrs Thiru")
      a["type"] = "test" # This is by default serializable
      
      def custom_serializer(obj):
          if isinstance(obj, Name):
              return obj.__dict__
      
      b = dumps(a) 
      # Produces TypeError, as the Name objects are not serializable
      b = dumps(a, default=custom_serializer)
      # Produces desired output
      

      这个例子可以进一步扩展到更大的范围。我们甚至可以根据需要添加过滤器或修改值。只需在 custom_serializer 函数中添加一个 else 部分

      def custom_serializer(obj):
          if isinstance(obj, Name):
              return obj.__dict__
          else:
              # Will get into this if the value is not serializable by default 
              # and is not a Name class object
              return None
      

      如果是自定义序列化程序,顶部给出的函数应该是:

      from json import loads, dumps
      from collections import OrderedDict
      
      def custom_serializer(obj):
          if isinstance(obj, Name):
              return obj.__dict__
          else:
              # Will get into this if the value is not serializable by default 
              # and is also not a Name class object
              return None
      
      def to_dict(input_ordered_dict):
          return loads(dumps(input_ordered_dict, default=custom_serializer))
      

      【讨论】:

      • 如果没有为字典中的任何对象正确定义repr,这将中断。
      • 如果字典包含构造函数当前不在您的范围内的任何对象,或者如果它包含构造函数在您的范围内但名称不同的对象,它也会中断。
      • 如果您的 dict 包含不可 json 序列化的项目,这也会中断。
      【解决方案4】:

      我写了一个递归方法来将OrderedDict 转换为一个简单的字典。

      def recursive_ordered_dict_to_dict(ordered_dict):
          simple_dict = {}
      
          for key, value in ordered_dict.items():
              if isinstance(value, OrderedDict):
                  simple_dict[key] = recursive_ordered_dict_to_dict(value)
              else:
                  simple_dict[key] = value
      
          return simple_dict
      

      注意:OrderedDicts 和 dicts 通常可以互换,但我在使用 pytest 在两种类型之间运行 assert 时遇到了问题。

      【讨论】:

        【解决方案5】:

        注意:此答案仅部分正确,请查看https://stackoverflow.com/a/25057250/1860929 以了解更多关于为什么字典大小相同的信息。

        原始答案

        这并没有回答转换的问题,它更多的是关于需要做什么。

        OrderedDict 是 Dict 大小两倍的基本假设是有缺陷的。检查这个:

        import sys
        import random
        from collections import OrderedDict
        
        test_dict = {}
        test_ordered_dict = OrderedDict()
        
        for key in range(10000):
            test_dict[key] = random.random()
            test_ordered_dict[key] = random.random()
        
        sys.getsizeof(test_dict)
        786712
        
        sys.getsizeof(test_ordered_dict)
        786712
        

        基本上两者大小相同。

        但是,操作所花费的时间并不相同,实际上,创建一个大字典(具有 100-10000 个键)比创建具有相同键的 OrderedDict 快 7-8 倍。 (在ipython 中使用%timeit 验证)

        import sys
        import random
        from collections import OrderedDict
        
        
        def operate_on_dict(r):
            test_dict = {}
            for key in range(r):
                test_dict[key] = random.random()
        
        def operate_on_ordered_dict(r):
            test_ordered_dict = OrderedDict()
            for key in range(r):
                test_ordered_dict[key] = random.random()
        
        %timeit for x in range(100): operate_on_ordered_dict(100)
        100 loops, best of 3: 9.24 ms per loop
        
        %timeit for x in range(100): operate_on_dict(100)
        1000 loops, best of 3: 1.23 ms per loop
        

        因此,IMO,您应该专注于将数据直接读入 dict 并对其进行操作,而不是先创建 OrderedDict 然后重复将其转换为字典。

        【讨论】:

        • 有趣。我的观点基于another SO answer,它指出 "(an OrderedDict) 并没有慢很多,但至少比使用普通的dict" 内存增加了一倍。不过我别无选择,因为 OrderedDict 是从我无法控制的函数返回的。
        • @WoJ 轮到我说有趣了 :) 这个答案来自编写 TimSort 的 Tim Peters,我现在真的被打败了。也问过question
        【解决方案6】:

        这应该可行:

        import collections
        
        def deep_convert_dict(layer):
            to_ret = layer
            if isinstance(layer, collections.OrderedDict):
                to_ret = dict(layer)
        
            try:
                for key, value in to_ret.items():
                    to_ret[key] = deep_convert_dict(value)
            except AttributeError:
                pass
        
            return to_ret
        

        尽管正如 jonrsharpe 所提到的,可能没有理由这样做——OrderedDict(按设计)在 dict 所在的任何地方都可以工作。

        【讨论】:

        • 谢谢 - 它适用于我的示例。我将不得不考虑如何容纳 dicts 列表(我发现我在获得的数据中也有)
        • @WoJ 这个解决方案适用于任何类型的嵌套迭代,无论它们是否是字典。
        • @PatrickCollins 他们希望通过字典列表递归
        猜你喜欢
        • 2019-10-22
        • 2019-01-19
        • 2021-06-02
        • 2021-12-01
        • 1970-01-01
        • 2021-08-24
        • 2020-07-21
        • 1970-01-01
        相关资源
        最近更新 更多