WoJ*_*WoJ 14 python dictionary
我有一个嵌套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))
Run Code Online (Sandbox Code Playgroud)
输出:
{u'name': u'Bob', u'ID': OrderedDict([(u'type', u'passport'), (u'nr', u'567')])}
Run Code Online (Sandbox Code Playgroud)
是否有直接的方法来转换所有出现的?
thi*_*dam 28
最简单的解决方案是使用json转储和加载
from json import loads, dumps
from collections import OrderedDict
def to_dict(input_ordered_dict):
return loads(dumps(input_ordered_dict))
Run Code Online (Sandbox Code Playgroud)
注意:上面的代码适用于json已知为可序列化对象的字典.可在此处找到默认对象类型列表
因此,如果有序字典不包含特殊值,这应该足够了.
编辑:根据评论,让我们改进上面的代码.让我们说,input_ordered_dict
可能包含默认情况下json无法序列化的自定义类对象.在那种情况下,我们应该使用我们的自定义序列化器的default
参数json.dumps
.
(例如):
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
Run Code Online (Sandbox Code Playgroud)
这个例子可以进一步扩展到更大的范围.我们甚至可以根据需要添加过滤器或修改值.只需在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
Run Code Online (Sandbox Code Playgroud)
在自定义序列化程序的情况下,顶部给出的函数应该是:
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))
Run Code Online (Sandbox Code Playgroud)
这应该工作:
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
Run Code Online (Sandbox Code Playgroud)
尽管,正如jonrsharpe所提到的那样,可能没有理由这样做-无论如何,OrderedDict
(按设计)都可以工作dict
。
您应该利用 Python 的内置copy
机制。
您可以OrderedDict
通过 Pythoncopyreg
模块(也由pickle
)覆盖复制行为。然后你可以使用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
Run Code Online (Sandbox Code Playgroud)
仅通过使用 Python 的内置复制基础设施,该解决方案就优于此处提供的所有其他答案,具体表现如下:
不仅仅适用于 JSON 数据。
不需要您为每种可能的元素类型(例如,等)实现list
特殊tuple
逻辑
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']
Run Code Online (Sandbox Code Playgroud)
由于我们的解决方案基于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))
Run Code Online (Sandbox Code Playgroud)
original: C(a=OrderedDict([(1, 'one'), (2, 'two')]))
converted: C(a={1: 'one', 2: 'two'})
Run Code Online (Sandbox Code Playgroud)