Как преобразовать вложенный словарь Python в объект?
Я ищу элегантный способ доступа к данным в словаре, содержащем вложенные словари и списки, используя синтаксис, похожий на объекты в JavaScript.
Например:
d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
Я хотел бы получить доступ к данным следующим образом:
x = dict2obj(d)
x.a # 1
x.b.c # 2
x.d[1].foo # bar
Мне кажется, это невозможно без использования рекурсии, но какой был бы хороший способ получить стиль доступа к объектам для словарей?
5 ответ(ов)
В вашем коде реализация класса obj
позволяет динамически создавать атрибуты на основе ключей словаря. Давайте подробно разберем, как это работает и что происходит в каждом шаге:
Конструктор
__init__
классаobj
:- Этот метод принимает словарь
d
в качестве аргумента. - Затем он итерируется по паре ключ-значение
k, v
в этом словаре.
- Этот метод принимает словарь
Условие для ключей:
- Если ключ
k
является списком или кортежем (isinstance(k, (list, tuple))
), то для каждого элементаx
в соответствующем значенииv
, еслиx
является словарем, создается новый объектobj
с помощью рекурсии (obj(x)
), иначе элемент сохраняется без изменений. - Если
k
не является списком или кортежем, проверяется, является ли значениеv
словарем. Если да, то создается новый объектobj
, в противном случае значение сохраняется как есть.
- Если ключ
Пример использования:
- При создании экземпляра
x
с помощью словаряd = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
, атрибутыa
,b
иd
будут созданы:x.a
будет равно1
x.b
будет объектомobj
, который содержитc
со значением2
, и его можно получить черезx.b.c
x.d
будет списком, где второй элемент — это объектobj
, содержащий ключfoo
со значением"bar"
, который можно получить черезx.d[1].foo
- При создании экземпляра
Таким образом, данный класс позволяет создавать многоуровневые структуры объектов на основе вложенных словарей и списков, а доступ к свойствам становится простым и интуитивно понятным.
Вот пример использования в вашем коде:
d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
x = obj(d)
print(x.b.c) # Вывод: 2
print(x.d[1].foo) # Вывод: 'bar'
Этот подход может быть полезен для работы с JSON-подобными структурами, где данные представлены в виде вложенных словарей и списков.
Для того чтобы создать новый объект на основе словаря с помощью функции type
, вы можете использовать следующий код:
x = type('new_dict', (object,), d)
Теперь добавим рекурсию в эту функцию, чтобы она могла обрабатывать вложенные словари и другие структуры данных.
Редактирование: Вот как я бы это реализовал:
>>> d
{'a': 1, 'b': {'c': 2}, 'd': ['hi', {'foo': 'bar'}]}
>>> def obj_dic(d):
top = type('new', (object,), d)
seqs = (tuple, list, set, frozenset)
for i, j in d.items():
if isinstance(j, dict):
setattr(top, i, obj_dic(j))
elif isinstance(j, seqs):
setattr(top, i,
type(j)(obj_dic(sj) if isinstance(sj, dict) else sj for sj in j))
else:
setattr(top, i, j)
return top
>>> x = obj_dic(d)
>>> x.a
1
>>> x.b.c
2
>>> x.d[1].foo
'bar'
В этом коде мы определяем функцию obj_dic
, которая принимает словарь d
и создает новый класс на его основе. Если элемент в словаре является вложенным словарем или коллекцией (списком, кортежем и т.д.), мы рекурсивно преобразуем его в новый объект. Это позволяет вам делать доступ к элементам, как если бы они были атрибутами объекта.
Этот код представляет собой реализацию класса Struct
, который позволяет создавать удобные объекты из вложенных структур данных, таких как словари и списки, как в Python 2, так и в Python 3.
Описание
Класс Struct
может использоваться для создания объектов, структура которых соответствует структуре переданного словаря. Например, вы можете передать вложенные словари, и для каждой пары ключ-значение автоматически создаются соответствующие атрибуты объекта.
Примеры использования
Python 3
data = {
'name': 'John',
'age': 30,
'attributes': {
'height': 180,
'weight': 75
}
}
person = Struct(data)
print(person.name) # Вывод: John
print(person.attributes.height) # Вывод: 180
Python 2
data = {
'name': 'John',
'age': 30,
'attributes': {
'height': 180,
'weight': 75
}
}
person = Struct(data)
print person.name # Вывод: John
print person.attributes.height # Вывод: 180
Как это работает
- Конструктор
__init__
принимает на вход словарьdata
и создает атрибуты для каждого элемента в этом словаре. - Метод
_wrap
обрабатывает вложенные структуры:- Если значение является списком, кортежем, множеством или неизменяемым множеством, оно будет рекурсивно обработано.
- Если значение является словарем, создается новый объект
Struct
. - В противном случае возвращается само значение.
Примечание
Класс Struct
можно использовать с любой последовательностью, словарем или значением любой глубины, благодаря рекурсивной обработке. Это делает ваш код более читаемым и позволяет работать с комплексными структурами данных, как с атрибутами объекта.
Вы можете использовать модуль json
из стандартной библиотеки вместе с пользовательским обработчиком объектов:
import json
class DictObject(object):
def __init__(self, dict_):
self.__dict__.update(dict_)
@classmethod
def from_dict(cls, d):
return json.loads(json.dumps(d), object_hook=DictObject)
Пример использования:
>>> d = {'a': 1, 'b': {'c': 2}, 'd': ['hi', {'foo': 'bar'}]}
>>> o = DictObject.from_dict(d)
>>> o.a
1
>>> o.b.c
2
>>> o.d[0]
'hi'
>>> o.d[1].foo
'bar'
Обратите внимание, что это не строго неизменяемо, как в случае с namedtuple
, т.е. вы можете изменять значения — но не структуру:
>>> o.b.c = 3
>>> o.b.c
3
Таким образом, DictObject
предоставляет удобный способ работы с данными в формате JSON, позволяя доступ к элементам как атрибутам объекта, с возможностью изменения значений.
Вот перевод на русский в стиле ответа на StackOverflow:
На основании предыдущих примеров, я разработал следующий класс, который позволяет строить и представлять объекты:
class Struct:
"""Рекурсивный класс для построения и представления объектов."""
def __init__(self, obj):
for k, v in obj.items():
if isinstance(v, dict):
setattr(self, k, Struct(v))
else:
setattr(self, k, v)
def __getitem__(self, val):
return self.__dict__[val]
def __repr__(self):
return '{%s}' % str(', '.join('%s : %s' % (k, repr(v)) for (k, v) in self.__dict__.items()))
Данный класс позволяет создать объект с помощью словаря, включая вложенные структуры. Пример использования:
data = {'name': 'Alice', 'age': 30, 'address': {'city': 'Moscow', 'zip': '123456'}}
struct_obj = Struct(data)
print(struct_obj) # Вывод: {name : 'Alice', age : 30, address : {city : 'Moscow', zip : '123456'}}
print(struct_obj['name']) # Вывод: Alice
Таким образом, вы получаете удобный способ работы с данными, используя простую и понятную структуру.
Итерация по словарям с использованием циклов 'for'
Создание словаря (dict) из отдельных списков ключей и значений
Преобразование списка словарей в DataFrame pandas
Почему использовать dict.get(key) вместо dict[key]?
Получить ключ по значению в словаре