如何将嵌套的 OrderedDict 转换为字典?

新手上路,请多包涵

我有一个嵌套的 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')])}

有没有直接的方法来转换所有的事件?

原文由 WoJ 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 614
2 个回答

您应该利用 Python 的内置 copy 机制。

您可以通过 Python 的 copyreg 模块(也被 pickle 使用)覆盖 OrderedDict 的复制行为。然后你可以使用 Python 的内置 copy.deepcopy() 函数来执行转换。

 import copy
import copyreg
from collections import OrderedDict

def convert_nested_ordered_dict(x):
    """
    Perform a deep copy of the given object, but convert
    all internal OrderedDicts to plain dicts along the way.

    Args:
        x: Any pickleable object

    Returns:
        A copy of the input, in which all OrderedDicts contained
        anywhere in the input (as iterable items or attributes, etc.)
        have been converted to plain dicts.
    """
    # Temporarily install a custom pickling function
    # (used by deepcopy) to convert OrderedDict to dict.
    orig_pickler = copyreg.dispatch_table.get(OrderedDict, None)
    copyreg.pickle(
        OrderedDict,
        lambda d: (dict, ([*d.items()],))
    )
    try:
        return copy.deepcopy(x)
    finally:
        # Restore the original OrderedDict pickling function (if any)
        del copyreg.dispatch_table[OrderedDict]
        if orig_pickler:
            copyreg.dispatch_table[OrderedDict] = orig_pickler

仅通过使用 Python 的内置复制基础结构,该解决方案就优于此处提供的所有其他答案,具体表现在以下方面:

  • 不仅仅适用于 JSON 数据。

  • 不需要您为每种可能的元素类型实现特殊逻辑(例如 listtuple 等)

  • deepcopy() 将正确处理集合中的重复引用:

   x = [1,2,3]
  d = {'a': x, 'b': x}
  assert d['a'] is d['b']

  d2 = copy.deepcopy(d)
  assert d2['a'] is d2['b']

由于我们的解决方案基于 deepcopy() 我们将拥有相同的优势。

  • 该解决方案还转换恰好是 OrderedDict 的 _属性_,而不仅仅是集合元素:
   class C:
      def __init__(self, a):
          self.a = a

      def __repr__(self):
          return f"C(a={self.a})"

  c = C(OrderedDict([(1, 'one'), (2, 'two')]))
  print("original: ", c)
  print("converted:", convert_nested_ordered_dict(c))

   original:  C(a=OrderedDict([(1, 'one'), (2, 'two')]))
  converted: C(a={1: 'one', 2: 'two'})

原文由 Stuart Berg 发布,翻译遵循 CC BY-SA 4.0 许可协议

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

from json import loads, dumps
from collections import OrderedDict

def to_dict(input_ordered_dict):
    return loads(dumps(input_ordered_dict))

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

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

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

(例如):

 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))

原文由 thiruvenkadam 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题