[英]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.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))
這應該有效:
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 數據。
不需要您為每種可能的元素類型(例如list
、 tuple
等)實現特殊邏輯
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
注意: OrderedDict
和dict
通常可以互換,但是在使用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.