Перетворити вкладений набір Python в об’єкт?


537

Я шукаю елегантний спосіб отримати дані за допомогою доступу до атрибутів на дікт з вкладеними диктами та списками (тобто синтаксисом об'єкта в стилі 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

Я думаю, це неможливо без рекурсії, але який би був приємний спосіб отримати об’єктний стиль для диктів?


6
Нещодавно я намагався зробити щось подібне, але повторюваний ключ словника ("від" - це ключове слово Python) не дозволив мені пройти з ним. Тому що як тільки ви спробуєте використовувати "x.from" для доступу до цього атрибуту, ви отримаєте синтаксичну помилку.
Dawie Strauss

3
це справді проблема, але я можу відмовитися від "від", щоб полегшити життя у доступі до великих конструкцій :) :) введення x ['a'] ['d'] [1] ['foo'] справді дратує, тому xad [1]. Правила правила. якщо вам потрібно, ви можете отримати доступ до нього через getattr (x, 'from') або використовувати _from як атрибут.
Марк

5
from_а не _fromвідповідно до PEP 8 .
Кос

1
Ви можете використовувати getattr(x, 'from')замість перейменування атрибута.
Джордж В. Рейлі

3
Більшість цих "рішень", здається, не спрацьовують (навіть прийняте, не дозволяє вкладатись d1.b.c), я думаю, зрозуміло, що вам слід використовувати щось із бібліотеки, наприклад, іменування з колекції , як підказує ця відповідь . .
Енді Хейден

Відповіді:


659

Оновлення: У 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

19
Те саме тут - це особливо корисно для реконструкції об'єктів Python з баз даних, орієнтованих на документи, як MongoDB.
mikemaccana

13
Для кращого друку додайте: def repr __ (self): return '<% s>'% str ('\ n' .join ('% s:% s'% (k, repr (v)) for (k, v ) in self .__ dict .iteritems ()))
шість8

15
Чи буде це працювати з вкладеними словниками? і дикти, що містять об'єкти та / або список тощо. Чи є якісь уловки?
Сем Столінга,

5
@Sam S: він не створюватиме вкладені Structs із вкладених словників, але загалом тип значення може бути будь-яким. Ключ обмежений тим, що він підходить для слота об'єкта
Елі Бендерський,

51
-1 , тому що а) вона не працює для вкладеної dicts, що це питання чітко просило, і б) в даний час існує та сама функціональність в стандартних бібліотеках argparse.Namespace(які також певних __eq__, __ne__, __contains__).
Вім

110
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'

7
Добре! Я б замінив .items () на .iteritems (), хоча для меншого сліду пам’яті.
Ерік О Лебігот

7
Якщо це не вимога ОП, це не проблема - але зауважте, що це не буде рекурсивно обробляти об'єкти у списках у списках.
Анонс

3
Я усвідомлюю, що це стара відповідь, але в наші дні було б краще використовувати абстрактний базовий клас замість цього потворного рядкаif isinstance(b, (list, tuple)):
wim

4
Порада: Запропонуйте objуспадкувати клас argparse.Namespaceдля додаткових функцій, таких як читабельне представлення рядків.
Серрано

2
В залежності від випадку використання, як правило , ми б перевірити , що це НЕ строковий тип , але що вона є тип послідовності
Вім

107

Дивно, але ніхто не згадував Бун . Ця бібліотека призначена виключно для надання доступу до стилю атрибутів до об'єктів, що диктують, і робить саме те, що хоче ОП. Демонстрація:

>>> 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


16
І є сумісна з python 3 (здається, 2,6-3,4) гілка Bunch під назвою Munch: github.com/Infinidat/munch
codyzu

Пучок - найкраще рішення з усіх цих питань, оскільки він добре підтримує декілька типів серіалізації та підтримується. Чудово, дякую. Я б хотів, щоб версія python3 була названа однаково, але чому б ні.
Джонатан

4
Тож просто попередження, Бунк та Аттрактик з цього приводу дуже повільні. Вони витратили приблизно 1/3 та 1/2 мого запиту відповідно, коли вони побачили часте використання в нашій програмі. Однозначно не те, що ігнорувати. Більше про це поговоріть stackoverflow.com/a/31569634/364604 .
JayD3e

Хоча це дозволяє об'єктному доступу до диктів, він робить це через getattr . Це ускладнює самоаналіз у інтелектуальних REPL-файлах, таких як IPython.
GDorn

Цікаво, як це порівнюється з JSONpath github.com/kennknowles/python-jsonpath-rw
MarkHu

64
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'

1
Чому ви створюєте типи об’єктів, а не інстанціюєте їх? Не було б це більш логічно? Я маю на увазі, чому б не зробити top_instance = top()і повернути те, куди ти повертаєшся top?
млинець

6
Nice для даних «листя», але приклади зручно залишити з «гілочки» , як xі x.bповертають некрасиво<class '__main__.new'>
MarkHu

46

Є помічник колекції під назвою 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

28
Це не відповідає на питання про рекурсію для вкладених диктів.
пристрасний

4
ви не можете змінювати назви.
Макс

Чи можемо ми гарантувати, що порядок списку, повернутого ключами () та значеннями (), відповідає порядку? Я маю на увазі, якщо це OrdenDict, так. Але стандартний дікт? Я знаю, що CPython 3.6+ та PyPy "компактні" дикти впорядковані, але цитуючи документацію: "Аспект, що зберігає замовлення в цій новій реалізації, вважається деталлю реалізації та на нього не слід покладатися"
Havok

38
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

Може використовуватися з будь-якою структурою послідовності / диктату / значення будь-якої глибини.


4
Це має бути відповіддю. Добре працює для гніздування. Ви можете використовувати це як object_hook також для json.load ().
010110110101

2
Подібно до функціональної відповіді SilentGhost з 2009 року - дані вузла листів доступні, але батьківські / гілочки відображаються як посилання на об'єкти. Для симпатичного друку,def __repr__(self): return '{%s}' % str(', '.join("'%s': %s" % (k, repr(v)) for (k, v) in self.__dict__.iteritems()))
MarkHu

5
Користувачі Python 3.x: це просто .items()замість .iteritems()рядка 4. (Функція була перейменована, але суттєво однакова )
neo post modern

Ідеально підходить для вкладених об'єктів, дякую! Як коментар новачки, як я, для python3 iteritems () потрібно змінити на items ()
Óscar Andreu

31

Оскільки я вважаю, що це найкращі аспекти попередніх прикладів, ось що я придумав:

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()))

Зауважте, що конструктор може бути скорочений до: def __init__(self, dct): for k, v in dct.iteritems(): setattr(self, k, isinstance(v, dict) and self.__class__(v) or v) що також видаляє явний заклик доStruct
Джордж В. Рейлі

2
Я б краще не оскаржував власну відповідь, але, озираючись на це, я помітив, що він не повторюється у типи послідовностей. xd [1] .foo в цьому випадку не вдається.
andyvanee

2
isinstance(v, dict)перевірка буде краще, isinstance(v, collections.Mapping)так що він може обробляти майбутнє диктує подібні речі
варильні панелі

29

Якщо ваш дикт походить json.loads(), ви можете перетворити його на об'єкт (а не в дикт) одним рядком:

import json
from collections import namedtuple

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

Див. Також Як перетворити дані JSON в об’єкт Python .


Здається, це набагато повільніше, ніж регулярно, .loadsякщо у вас є величезний об’єкт JSON
michaelsnowden,

16

Якщо ви хочете отримати доступ до ключів dict як об'єкт (або як диктат для складних ключів), зробіть це рекурсивно, а також зможете оновити вихідний дікт, ви можете зробити:

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
>>> 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'

12

Я в кінцевому підсумку намагався БУТИ AttrDict і Bunchбібліотеки та виявили їх занадто повільними для мого використання. Після того, як ми з другом подивилися, ми виявили, що основний метод написання цих бібліотек призводить до того, що бібліотека агресивно повторюється через вкладений об'єкт і робить копії словникового об’єкта впродовж усього. Зважаючи на це, ми внесли дві ключові зміни. 1) Ми зробили атрибути ліниво завантаженими 2) замість того, щоб створювати копії об’єкта словника, ми створюємо копії легкого проксі-об’єкта. Це остаточна реалізація. Підвищення продуктивності використання цього коду неймовірне. Під час використання AttrDict або Bunch, ці дві бібліотеки споживали 1/2 та 1/3 відповідно мого часу запиту (що !?). Цей код скоротив цей час майже нічого (десь в межах 0,5 мс). Це, звичайно, залежить від ваших потреб,

class DictProxy(object):
    def __init__(self, obj):
        self.obj = obj

    def __getitem__(self, key):
        return wrap(self.obj[key])

    def __getattr__(self, key):
        try:
            return wrap(getattr(self.obj, key))
        except AttributeError:
            try:
                return self[key]
            except KeyError:
                raise AttributeError(key)

    # you probably also want to proxy important list properties along like
    # items(), iteritems() and __len__

class ListProxy(object):
    def __init__(self, obj):
        self.obj = obj

    def __getitem__(self, key):
        return wrap(self.obj[key])

    # you probably also want to proxy important list properties along like
    # __iter__ and __len__

def wrap(value):
    if isinstance(value, dict):
        return DictProxy(value)
    if isinstance(value, (tuple, list)):
        return ListProxy(value)
    return value

Дивіться оригінальну реалізацію тут за посиланням https://stackoverflow.com/users/704327/michael-merickel .

Інша річ, яку слід зазначити, полягає в тому, що ця реалізація досить проста і не реалізує всіх методів, які вам можуть знадобитися. Вам потрібно буде записати такі, як потрібно, на об'єкти DictProxy або ListProxy.


9

x.__dict__.update(d) повинен робити добре.


дякую за вашу відповідь, але що таке х? dict чи стандартний об'єкт? ви можете мені підказати, будь ласка.
Марк

х - ваш об’єкт. Кожен об’єкт має дікт . Оновлення дікта об’єкта ви фактично оновлюєте ключові вари в ньому.
Алекс Родрігес

Сміливі слова - _ _ dict _ _
Alex Rodrigues

15
Це не буде обробляти вкладені словники.
FogleBird

Не у кожного об'єкта є __dict__: спробуйте, object().__dict__і ви отримаєтеAttributeError: 'object' object has no attribute '__dict__'
Буде Манлі

8

Ви можете використовувати jsonмодуль стандартної бібліотеки за допомогою спеціального гака об'єкта :

import json

class obj(object):
    def __init__(self, dict_):
        self.__dict__.update(dict_)

def dict2obj(d):
    return json.loads(json.dumps(d), object_hook=obj)

Приклад використання:

>>> d = {'a': 1, 'b': {'c': 2}, 'd': ['hi', {'foo': 'bar'}]}
>>> o = dict2obj(d)
>>> o.a
1
>>> o.b.c
2
>>> o.d[0]
u'hi'
>>> o.d[1].foo
u'bar'

І це не суворо лише для читання, як це відбувається namedtuple, тобто ви можете змінювати значення - не структуру:

>>> o.b.c = 3
>>> o.b.c
3

1
Найкраща відповідь на сьогодні
Коннор

Мені подобається ідея використовувати механізм завантаження json для вкладених елементів. Але у нас вже є дикт, і мені не подобається той факт, що потрібно створити з нього рядок, щоб відобразити його в об'єкт. Я б хотів мати рішення, яке створює об'єкти безпосередньо з диктату.
Сандро

1
Потрібно перетворити в рядок першим, але досить загальним, оскільки можна було би розширити його через json.JSONEncoderта object_hook.
Сандро

6

З цього слід почати:

class dict2obj(object):
    def __init__(self, d):
        self.__dict__['d'] = d

    def __getattr__(self, key):
        value = self.__dict__['d'][key]
        if type(value) == type({}):
            return dict2obj(value)

        return value

d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}

x = dict2obj(d)
print x.a
print x.b.c
print x.d[1].foo

Він ще не працює для списків. Вам доведеться загортати списки в UserList і перевантажувати __getitem__диски.


2
Щоб він працював для списків, використовуйте if isinstance(d, list)пункт із Anonвідповіді 's.
Vinay Sajip

6

Я знаю, що тут вже багато відповідей, і я запізнююсь на вечірку, але цей метод буде рекурсивно і "на місці" перетворити словник в об'єктоподібну структуру ... Працює в 3.xx

def dictToObject(d):
    for k,v in d.items():
        if isinstance(v, dict):
            d[k] = dictToObject(v)
    return namedtuple('object', d.keys())(*d.values())

# Dictionary created from JSON file
d = {
    'primaryKey': 'id', 
    'metadata': 
        {
            'rows': 0, 
            'lastID': 0
        }, 
    'columns': 
        {
            'col2': {
                'dataType': 'string', 
                'name': 'addressLine1'
            }, 
            'col1': {
                'datatype': 'string', 
                'name': 'postcode'
            }, 
            'col3': {
                'dataType': 'string', 
                'name': 'addressLine2'
            }, 
            'col0': {
                'datatype': 'integer', 
                'name': 'id'
            }, 
            'col4': {
                'dataType': 'string', 
                'name': 'contactNumber'
            }
        }, 
        'secondaryKeys': {}
}

d1 = dictToObject(d)
d1.columns.col1 # == object(datatype='string', name='postcode')
d1.metadata.rows # == 0

Що ви маєте на увазі "об'єктоподібна структура"?
MoralCode

1
Об'єктно-подібний через принцип введення качки. Що я маю на увазі, це ви можете отримати доступ до його властивостей так, як би ви були, якби це був екземпляр класу. але це НЕ об’єкт у цьому сенсі, це названий кортеж.
Айдан Хаддон-Райт

5
from mock import Mock
d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
my_data = Mock(**d)

# We got
# my_data.a == 1

4

Дозвольте пояснити рішення, яке я майже використовував деякий час тому. Але по-перше, причину цього я не пояснював той факт, що наступний код:

d = {'from': 1}
x = dict2obj(d)

print x.from

видає цю помилку:

  File "test.py", line 20
    print x.from == 1
                ^
SyntaxError: invalid syntax

Оскільки "від" є ключовим словом Python, є певні ключі словника, які ви не можете дозволити.


Тепер моє рішення дозволяє отримати доступ до елементів словника, використовуючи безпосередньо їх імена. Але це також дозволяє використовувати «словникову семантику». Ось код із прикладом використання:

class dict2obj(dict):
    def __init__(self, dict_):
        super(dict2obj, self).__init__(dict_)
        for key in self:
            item = self[key]
            if isinstance(item, list):
                for idx, it in enumerate(item):
                    if isinstance(it, dict):
                        item[idx] = dict2obj(it)
            elif isinstance(item, dict):
                self[key] = dict2obj(item)

    def __getattr__(self, key):
        return self[key]

d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}

x = dict2obj(d)

assert x.a == x['a'] == 1
assert x.b.c == x['b']['c'] == 2
assert x.d[1].foo == x['d'][1]['foo'] == "bar"

1
Структура класу: def init __ (self, ** записи): self .__ dict .update (entries)
Кеннет Рейц

4

Старі запитання, але я можу щось більше поговорити. Здається, ніхто не говорить про рекурсивний дикт. Це мій код:

#!/usr/bin/env python

class Object( dict ):
    def __init__( self, data = None ):
        super( Object, self ).__init__()
        if data:
            self.__update( data, {} )

    def __update( self, data, did ):
        dataid = id(data)
        did[ dataid ] = self

        for k in data:
            dkid = id(data[k])
            if did.has_key(dkid):
                self[k] = did[dkid]
            elif isinstance( data[k], Object ):
                self[k] = data[k]
            elif isinstance( data[k], dict ):
                obj = Object()
                obj.__update( data[k], did )
                self[k] = obj
                obj = None
            else:
                self[k] = data[k]

    def __getattr__( self, key ):
        return self.get( key, None )

    def __setattr__( self, key, value ):
        if isinstance(value,dict):
            self[key] = Object( value )
        else:
            self[key] = value

    def update( self, *args ):
        for obj in args:
            for k in obj:
                if isinstance(obj[k],dict):
                    self[k] = Object( obj[k] )
                else:
                    self[k] = obj[k]
        return self

    def merge( self, *args ):
        for obj in args:
            for k in obj:
                if self.has_key(k):
                    if isinstance(self[k],list) and isinstance(obj[k],list):
                        self[k] += obj[k]
                    elif isinstance(self[k],list):
                        self[k].append( obj[k] )
                    elif isinstance(obj[k],list):
                        self[k] = [self[k]] + obj[k]
                    elif isinstance(self[k],Object) and isinstance(obj[k],Object):
                        self[k].merge( obj[k] )
                    elif isinstance(self[k],Object) and isinstance(obj[k],dict):
                        self[k].merge( obj[k] )
                    else:
                        self[k] = [ self[k], obj[k] ]
                else:
                    if isinstance(obj[k],dict):
                        self[k] = Object( obj[k] )
                    else:
                        self[k] = obj[k]
        return self

def test01():
    class UObject( Object ):
        pass
    obj = Object({1:2})
    d = {}
    d.update({
        "a": 1,
        "b": {
            "c": 2,
            "d": [ 3, 4, 5 ],
            "e": [ [6,7], (8,9) ],
            "self": d,
        },
        1: 10,
        "1": 11,
        "obj": obj,
    })
    x = UObject(d)


    assert x.a == x["a"] == 1
    assert x.b.c == x["b"]["c"] == 2
    assert x.b.d[0] == 3
    assert x.b.d[1] == 4
    assert x.b.e[0][0] == 6
    assert x.b.e[1][0] == 8
    assert x[1] == 10
    assert x["1"] == 11
    assert x[1] != x["1"]
    assert id(x) == id(x.b.self.b.self) == id(x.b.self)
    assert x.b.self.a == x.b.self.b.self.a == 1

    x.x = 12
    assert x.x == x["x"] == 12
    x.y = {"a":13,"b":[14,15]}
    assert x.y.a == 13
    assert x.y.b[0] == 14

def test02():
    x = Object({
        "a": {
            "b": 1,
            "c": [ 2, 3 ]
        },
        1: 6,
        2: [ 8, 9 ],
        3: 11,
    })
    y = Object({
        "a": {
            "b": 4,
            "c": [ 5 ]
        },
        1: 7,
        2: 10,
        3: [ 12 , 13 ],
    })
    z = {
        3: 14,
        2: 15,
        "a": {
            "b": 16,
            "c": 17,
        }
    }
    x.merge( y, z )
    assert 2 in x.a.c
    assert 3 in x.a.c
    assert 5 in x.a.c
    assert 1 in x.a.b
    assert 4 in x.a.b
    assert 8 in x[2]
    assert 9 in x[2]
    assert 10 in x[2]
    assert 11 in x[3]
    assert 12 in x[3]
    assert 13 in x[3]
    assert 14 in x[3]
    assert 15 in x[2]
    assert 16 in x.a.b
    assert 17 in x.a.c

if __name__ == '__main__':
    test01()
    test02()

4

Хотіла завантажити мою версію цієї маленької парадигми.

class Struct(dict):
  def __init__(self,data):
    for key, value in data.items():
      if isinstance(value, dict):
        setattr(self, key, Struct(value))
      else:   
        setattr(self, key, type(value).__init__(value))

      dict.__init__(self,data)

Він зберігає атрибути для типу, який імпортується в клас. Моє єдине занепокоєння - це перезаписування методів із словника вашого розбору. Але інакше здається солідним!


Хоча це гарна ідея, це, здається, не працює на прикладі ОП, насправді це, здається, модифікує передане в словнику! Насправді df.aце навіть не працює.
Енді Хейден

4

Це теж добре працює

class DObj(object):
    pass

dobj = Dobj()
dobj.__dict__ = {'a': 'aaa', 'b': 'bbb'}

print dobj.a
>>> aaa
print dobj.b
>>> bbb

3

Ось ще один спосіб реалізації оригінальної пропозиції SilentGhost:

def dict2obj(d):
  if isinstance(d, dict):
    n = {}
    for item in d:
      if isinstance(d[item], dict):
        n[item] = dict2obj(d[item])
      elif isinstance(d[item], (list, tuple)):
        n[item] = [dict2obj(elem) for elem in d[item]]
      else:
        n[item] = d[item]
    return type('obj_from_dict', (object,), n)
  else:
    return d

3

Я натрапив на випадок, що мені потрібно було рекурсивно перетворювати список диктовок у список об’єктів, тому на основі фрагмента Роберто тут зробила роботу для мене:

def dict2obj(d):
    if isinstance(d, dict):
        n = {}
        for item in d:
            if isinstance(d[item], dict):
                n[item] = dict2obj(d[item])
            elif isinstance(d[item], (list, tuple)):
                n[item] = [dict2obj(elem) for elem in d[item]]
            else:
                n[item] = d[item]
        return type('obj_from_dict', (object,), n)
    elif isinstance(d, (list, tuple,)):
        l = []
        for item in d:
            l.append(dict2obj(item))
        return l
    else:
        return d

Зауважте, що будь-який кортеж буде перетворений у його еквівалентний список із зрозумілих причин.

Сподіваюся, це допомагає комусь стільки, скільки всі ваші відповіді зробили для мене, хлопці.


3

Як щодо просто призначення вашого dictна __dict__порожній об'єкт?

class Object:
    """If your dict is "flat", this is a simple way to create an object from a dict

    >>> obj = Object()
    >>> obj.__dict__ = d
    >>> d.a
    1
    """
    pass

Звичайно, це не вдається на прикладі вашого вкладеного дикту, якщо ви не будете рекурсивно виконувати:

# For a nested dict, you need to recursively update __dict__
def dict2obj(d):
    """Convert a dict to an object

    >>> d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
    >>> obj = dict2obj(d)
    >>> obj.b.c
    2
    >>> obj.d
    ["hi", {'foo': "bar"}]
    """
    try:
        d = dict(d)
    except (TypeError, ValueError):
        return d
    obj = Object()
    for k, v in d.iteritems():
        obj.__dict__[k] = dict2obj(v)
    return obj

І ваш елемент списку прикладів, ймовірно, мав на увазі бути Mappingсписком (ключ, значення) таких пар:

>>> d = {'a': 1, 'b': {'c': 2}, 'd': [("hi", {'foo': "bar"})]}
>>> obj = dict2obj(d)
>>> obj.d.hi.foo
"bar"

2

Ось ще одна реалізація:

class DictObj(object):
    def __init__(self, d):
        self.__dict__ = d

def dict_to_obj(d):
    if isinstance(d, (list, tuple)): return map(dict_to_obj, d)
    elif not isinstance(d, dict): return d
    return DictObj(dict((k, dict_to_obj(v)) for (k,v) in d.iteritems()))

[Редагувати] Пропущений біт щодо також керування диктами в списках, а не лише іншими диктами. Додано виправлення.


Зауважте, що встановлення dict у вихідному словнику означає, що будь-які зміни атрибутів на отриманому об'єкті також впливатимуть на словник, який створив об'єкт, і навпаки. Це може призвести до несподіваних результатів, якщо словник використовується для чогось іншого, ніж для створення об'єкта.
Марк Родді

@Mark: Насправді новий словник передається DictObj кожен раз, а не просто проходить через один і той же об’єкт dict, тому насправді це не відбудеться. Зробити це потрібно, оскільки мені потрібно перекласти значення і в словник, тому неможливо було б пройти через оригінальний об'єкт dict, не змінюючи його сам.
Брайан

На це є досить багато відповідей, прийнята відповідь не виглядала так, як це було б рекурсивним або обробляти списки. Я перечитав їх усі, і цей на перший погляд виглядав найпростішим, я тестував, і це спрацювало. Чудова відповідь.
Завжди тренується

2
class Struct(dict):
    def __getattr__(self, name):
        try:
            return self[name]
        except KeyError:
            raise AttributeError(name)

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

    def copy(self):
        return Struct(dict.copy(self))

Використання:

points = Struct(x=1, y=2)
# Changing
points['x'] = 2
points.y = 1
# Accessing
points['x'], points.x, points.get('x') # 2 2 2
points['y'], points.y, points.get('y') # 1 1 1
# Accessing inexistent keys/attrs 
points['z'] # KeyError: z
points.z # AttributeError: z
# Copying
points_copy = points.copy()
points.x = 2
points_copy.x # 1

2

Як щодо цього:

from functools import partial
d2o=partial(type, "d2o", ())

Потім це можна використовувати так:

>>> o=d2o({"a" : 5, "b" : 3})
>>> print o.a
5
>>> print o.b
3

2

Я думаю, що диктат складається з числа, рядка і диктату достатньо більшості часу. Тож я ігнорую ситуацію, що кортежі, списки та інші типи не фігурують у кінцевому вимірі дикта.

Враховуючи спадщину, поєднану з рекурсією, вона зручно вирішує проблему друку, а також забезпечує два способи запиту даних, один спосіб редагування даних.

Дивіться приклад нижче, диктант, який описує деяку інформацію про студентів:

group=["class1","class2","class3","class4",]
rank=["rank1","rank2","rank3","rank4","rank5",]
data=["name","sex","height","weight","score"]

#build a dict based on the lists above
student_dic=dict([(g,dict([(r,dict([(d,'') for d in data])) for r in rank ]))for g in group])

#this is the solution
class dic2class(dict):
    def __init__(self, dic):
        for key,val in dic.items():
            self.__dict__[key]=self[key]=dic2class(val) if isinstance(val,dict) else val


student_class=dic2class(student_dic)

#one way to edit:
student_class.class1.rank1['sex']='male'
student_class.class1.rank1['name']='Nan Xiang'

#two ways to query:
print student_class.class1.rank1
print student_class.class1['rank1']
print '-'*50
for rank in student_class.class1:
    print getattr(student_class.class1,rank)

Результати:

{'score': '', 'sex': 'male', 'name': 'Nan Xiang', 'weight': '', 'height': ''}
{'score': '', 'sex': 'male', 'name': 'Nan Xiang', 'weight': '', 'height': ''}
--------------------------------------------------
{'score': '', 'sex': '', 'name': '', 'weight': '', 'height': ''}
{'score': '', 'sex': '', 'name': '', 'weight': '', 'height': ''}
{'score': '', 'sex': 'male', 'name': 'Nan Xiang', 'weight': '', 'height': ''}
{'score': '', 'sex': '', 'name': '', 'weight': '', 'height': ''}
{'score': '', 'sex': '', 'name': '', 'weight': '', 'height': ''}

2

Зазвичай ви хочете відобразити ієрархію малюнків у вашому об'єкті, але не перелічити чи кортежі, які зазвичай знаходяться на найнижчому рівні. Ось так я це зробив:

class defDictToObject(object):

    def __init__(self, myDict):
        for key, value in myDict.items():
            if type(value) == dict:
                setattr(self, key, defDictToObject(value))
            else:
                setattr(self, key, value)

Отже, ми робимо:

myDict = { 'a': 1,
           'b': { 
              'b1': {'x': 1,
                    'y': 2} },
           'c': ['hi', 'bar'] 
         }

і отримуй:

x.b.b1.x 1

x.c ['привіт', 'бар']


1

Мій словник має такий формат:

addr_bk = {
    'person': [
        {'name': 'Andrew', 'id': 123, 'email': 'andrew@mailserver.com',
         'phone': [{'type': 2, 'number': '633311122'},
                   {'type': 0, 'number': '97788665'}]
        },
        {'name': 'Tom', 'id': 456,
         'phone': [{'type': 0, 'number': '91122334'}]}, 
        {'name': 'Jack', 'id': 7788, 'email': 'jack@gmail.com'}
    ]
}

Як видно, я вклав словники та список диктовок . Це тому, що addr_bk було декодовано з даних буфера протоколу, який перетворився на дію python за допомогою lwpb.codec. Є необов'язкові поля (наприклад, електронна пошта => там, де ключ може бути недоступний) та повторне поле (наприклад, телефон =>, перетворене на список діктів)

Я спробував усі вище запропоновані рішення. Деякі не впораються з вкладеними словниками добре. Інші не можуть легко надрукувати деталі об'єкта.

Тільки рішення, dict2obj (диктат) Даві Стросса, працює найкраще.

Я трохи вдосконалив його, щоб впоратися, коли ключ неможливо знайти:

# Work the best, with nested dictionaries & lists! :)
# Able to print out all items.
class dict2obj_new(dict):
    def __init__(self, dict_):
        super(dict2obj_new, self).__init__(dict_)
        for key in self:
            item = self[key]
            if isinstance(item, list):
                for idx, it in enumerate(item):
                    if isinstance(it, dict):
                        item[idx] = dict2obj_new(it)
            elif isinstance(item, dict):
                self[key] = dict2obj_new(item)

    def __getattr__(self, key):
        # Enhanced to handle key not found.
        if self.has_key(key):
            return self[key]
        else:
            return None

Потім я перевірив це:

# Testing...
ab = dict2obj_new(addr_bk)

for person in ab.person:
  print "Person ID:", person.id
  print "  Name:", person.name
  # Check if optional field is available before printing.
  if person.email:
    print "  E-mail address:", person.email

  # Check if optional field is available before printing.
  if person.phone:
    for phone_number in person.phone:
      if phone_number.type == codec.enums.PhoneType.MOBILE:
        print "  Mobile phone #:",
      elif phone_number.type == codec.enums.PhoneType.HOME:
        print "  Home phone #:",
      else:
        print "  Work phone #:",
      print phone_number.number

1

Створення моєї відповіді на " python: Як додати властивість до класу динамічно? ":

class data(object):
    def __init__(self,*args,**argd):
        self.__dict__.update(dict(*args,**argd))

def makedata(d):
    d2 = {}
    for n in d:
        d2[n] = trydata(d[n])
    return data(d2)

def trydata(o):
    if isinstance(o,dict):
        return makedata(o)
    elif isinstance(o,list):
        return [trydata(i) for i in o]
    else:
        return o

Ви закликаєте makedataдо словника, який ви хочете перетворити, або, можливо, trydataзалежно від того, що ви очікуєте в якості введення, і він випльовує об'єкт даних.

Примітки:

  • Ви можете додати elifs до, trydataякщо вам потрібна додаткова функціональність.
  • Очевидно, це не спрацює, якщо ви хочете x.a = {}чи подібне.
  • Якщо ви хочете прочитати лише версію, використовуйте дані класу з оригінальної відповіді .
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.