Подтвердить что ты не робот

Как преобразовать вложенный 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')])}

Есть ли прямой метод для преобразования всех вступлений?

4b9b3361

Ответ 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