Преобразовать вложенный Python dict в объект? - PullRequest
490 голосов
/ 20 августа 2009

Я ищу элегантный способ получения данных с использованием доступа к атрибутам в файле с некоторыми вложенными документами и списками (т. Е. Синтаксис объекта в стиле 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

Я думаю, это невозможно без рекурсии, но что было бы хорошим способом получить стиль объекта для диктов?

Ответы [ 34 ]

620 голосов
/ 20 августа 2009

Обновление: В Python 2.6 и более поздних версиях подумайте, соответствует ли структура данных namedtuple вашим потребностям:

>>> from collections import namedtuple
>>> MyStruct = namedtuple('MyStruct', 'a b d')
>>> s = MyStruct(a=1, b={'c': 2}, d=['hi'])
>>> s
MyStruct(a=1, b={'c': 2}, d=['hi'])
>>> s.a
1
>>> s.b
{'c': 2}
>>> s.c
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'MyStruct' object has no attribute 'c'
>>> s.d
['hi']

Альтернатива (исходное содержание ответа):

class Struct:
    def __init__(self, **entries):
        self.__dict__.update(entries)

Затем вы можете использовать:

>>> args = {'a': 1, 'b': 2}
>>> s = Struct(**args)
>>> s
<__main__.Struct instance at 0x01D6A738>
>>> s.a
1
>>> s.b
2
98 голосов
/ 20 августа 2009
class obj(object):
    def __init__(self, d):
        for a, b in d.items():
            if isinstance(b, (list, tuple)):
               setattr(self, a, [obj(x) if isinstance(x, dict) else x for x in b])
            else:
               setattr(self, a, obj(b) if isinstance(b, dict) else b)

>>> d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
>>> x = obj(d)
>>> x.b.c
2
>>> x.d[1].foo
'bar'
87 голосов
/ 20 июля 2014

Удивительно, но никто не упомянул Букет . Эта библиотека предназначена исключительно для предоставления доступа к объектам dict в стиле атрибутов и выполняет именно то, что хочет OP. Демонстрация:

>>> from bunch import bunchify
>>> d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
>>> x = bunchify(d)
>>> x.a
1
>>> x.b.c
2
>>> x.d[1].foo
'bar'

Библиотека Python 3 доступна по адресу https://github.com/Infinidat/munch - Кредит предоставляется codyzu

58 голосов
/ 20 августа 2009
x = type('new_dict', (object,), d)

затем добавьте рекурсию к этому, и все готово.

edit вот как я бы это реализовал:

>>> 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'
46 голосов
/ 23 февраля 2012

Там есть помощник по коллекции namedtuple, который может сделать это для вас:

from collections import namedtuple

d_named = namedtuple('Struct', d.keys())(*d.values())

In [7]: d_named
Out[7]: Struct(a=1, b={'c': 2}, d=['hi', {'foo': 'bar'}])

In [8]: d_named.a
Out[8]: 1
30 голосов
/ 04 июля 2011

Взяв то, что я чувствую, являются лучшими аспектами предыдущих примеров, вот что я придумал:

class Struct:
  '''The recursive class for building and representing objects with.'''
  def __init__(self, obj):
    for k, v in obj.iteritems():
      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__.iteritems()))
28 голосов
/ 09 августа 2011
class Struct(object):
    """Comment removed"""
    def __init__(self, data):
        for name, value in data.iteritems():
            setattr(self, name, self._wrap(value))

    def _wrap(self, value):
        if isinstance(value, (tuple, list, set, frozenset)): 
            return type(value)([self._wrap(v) for v in value])
        else:
            return Struct(value) if isinstance(value, dict) else value

Может использоваться с любой структурой последовательности / dict / value любой глубины.

26 голосов
/ 08 апреля 2013

Если ваш диктант исходит из json.loads(), вы можете вместо этого превратить его в объект (а не в диктат) в одну строку:

import json
from collections import namedtuple

json.loads(data, object_hook=lambda d: namedtuple('X', d.keys())(*d.values()))

См. Также Как преобразовать данные JSON в объект Python .

14 голосов
/ 23 марта 2013

Если вы хотите получить доступ к ключам диктовки как к объекту (или как к диктату для сложных клавиш), сделать это рекурсивно, а также иметь возможность обновить исходный дикт, вы можете сделать:

class Dictate(object):
    """Object view of a dict, updating the passed in dict when values are set
    or deleted. "Dictate" the contents of a dict...: """

    def __init__(self, d):
        # since __setattr__ is overridden, self.__dict = d doesn't work
        object.__setattr__(self, '_Dictate__dict', d)

    # Dictionary-like access / updates
    def __getitem__(self, name):
        value = self.__dict[name]
        if isinstance(value, dict):  # recursively view sub-dicts as objects
            value = Dictate(value)
        return value

    def __setitem__(self, name, value):
        self.__dict[name] = value
    def __delitem__(self, name):
        del self.__dict[name]

    # Object-like access / updates
    def __getattr__(self, name):
        return self[name]

    def __setattr__(self, name, value):
        self[name] = value
    def __delattr__(self, name):
        del self[name]

    def __repr__(self):
        return "%s(%r)" % (type(self).__name__, self.__dict)
    def __str__(self):
        return str(self.__dict)

Пример использования:

d = {'a': 'b', 1: 2}
dd = Dictate(d)
assert dd.a == 'b'  # Access like an object
assert dd[1] == 2  # Access like a dict
# Updates affect d
dd.c = 'd'
assert d['c'] == 'd'
del dd.a
del dd[1]
# Inner dicts are mapped
dd.e = {}
dd.e.f = 'g'
assert dd['e'].f == 'g'
assert d == {'c': 'd', 'e': {'f': 'g'}}
13 голосов
/ 20 августа 2009
>>> def dict2obj(d):
        if isinstance(d, list):
            d = [dict2obj(x) for x in d]
        if not isinstance(d, dict):
            return d
        class C(object):
            pass
        o = C()
        for k in d:
            o.__dict__[k] = dict2obj(d[k])
        return o


>>> 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'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...