Как преобразовать вложенный OrderedDict в 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))
Вывод:
{u'name': u'Bob', u'ID': OrderedDict([(u'type', u'passport'), (u'nr', u'567')])}
Есть ли прямой метод для преобразования всех вступлений?
Ответы
Ответ 1
Самое простое решение - использовать 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
с 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
Этот пример может быть расширен до гораздо большей области. Мы даже можем добавить фильтры или изменить значение нашей необходимости. Просто добавьте еще часть в функцию custom_serializer
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))
Ответ 2
Это должно работать:
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
делает.
Ответ 3
ПРИМЕЧАНИЕ. Этот ответ является лишь частично правильным, проверьте fooobar.com/questions/124894/..., чтобы узнать больше о том, почему dicts имеют одинаковые размеры.
Оригинальный ответ
Это не отвечает на вопрос об конверсии, а больше о том, что нужно сделать.
Основное предположение о том, что 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 ключами) примерно на 7-8 раз быстрее, чем создание OrderedDict с такими же ключами. (Проверено с помощью %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 повторно.
Ответ 4
Я написал рекурсивный метод для преобразования 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
обычно взаимозаменяемы, но я столкнулся с проблемой при выполнении assert
между двумя типами с использованием pytest
.
Ответ 5
Здесь версия, которая также обрабатывает списки и кортежи. В этом комментарии ФП упоминает, что списки диктов также имеют смысл.
Обратите внимание, что это также преобразует кортежи в списки. Сохранение кортежей оставлено в качестве упражнения для читателя :)
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