簡體   English   中英

如何將嵌套的 OrderedDict 轉換為 dict?

[英]how to convert a nested OrderedDict to dict?

我有一個嵌套的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))

Output:

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

有沒有直接的方法來轉換所有的出現?

最簡單的解決方案是使用 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.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))

這應該有效:

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任何地方工作。

注意:此答案僅部分正確,請查看https://stackoverflow.com/a/25057250/1860929以了解有關為什么 dict 具有相同大小的更多信息。

原答案

這並沒有回答轉換的問題,它更多的是關於需要做什么。

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 ipython

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然后將其重復轉換為 dict。

您應該利用 Python 的內置copy機制。

您可以通過 Python 的copyreg模塊(也由pickle使用)覆蓋OrderedDict的復制行為。 然后你可以使用 Python 的內置copy.deepcopy() function 來執行轉換。

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 id(d['a']) == id(d['b']) d2 = copy.deepcopy(d) assert id(d2['a']) == id(d2['b'])

    由於我們的解決方案基於deepcopy()我們將具有相同的優勢。

  • 此解決方案還轉換恰好是OrderedDict屬性,而不僅僅是集合元素:

     class C: def __init__(self, a=None, b=None): self.a = a or OrderedDict([(1, 'one'), (2, 'two')]) self.b = b or OrderedDict([(3, 'three'), (4, 'four')]) def __repr__(self): return f"C(a={self.a}, b={self.b})" print("original: ", C()) print("converted:", convert_nested_ordered_dict(C()))
     original: C(a=OrderedDict([(1, 'one'), (2, 'two')]), b=OrderedDict([(3, 'three'), (4, 'four')])) converted: C(a={1: 'one', 2: 'two'}, b={3: 'three', 4: 'four'})

我編寫了一個遞歸方法來將OrderedDict轉換為一個簡單的 dict。

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

注意: OrderedDictdict通常可以互換,但是在使用pytest在這兩種類型之間運行assert時我遇到了問題。

這是一個也處理列表和元組的版本。 在此評論中,OP 提到 dicts 列表也是一個需要處理的案例。

請注意,這也會將元組轉換為列表。 保留元組留給讀者作為練習:)

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 

此代碼應該適用於嵌套列表。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM