중첩 된 Python dict를 객체로 변환 하시겠습니까?


537

중첩 된 dicts 및 list가있는 dict의 속성 액세스를 사용하여 데이터를 얻는 우아한 방법을 찾고 있습니다 (예 : 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

나는 이것이 재귀 없이는 불가능하다고 생각하지만 dicts에 대한 객체 스타일을 얻는 좋은 방법은 무엇입니까?


6
최근 비슷한 작업을 시도했지만 반복 사전 키 ( "from"-Python 키워드)를 사용하지 못했습니다. "x.from"을 사용하여 해당 속성에 액세스하려고하면 구문 오류가 발생하기 때문입니다.
Dawie Strauss

3
그것은 실제로 문제이지만, 큰 dict 구문에 액세스하는 것이 인생을 더 쉽게 만들기 위해 "from"을 포기할 수 있습니다. [1] .foo 규칙. from이 필요한 경우 getattr (x, 'from')을 통해 액세스하거나 대신 _from을 속성으로 사용할 수 있습니다.
Marc

5
from_PEP 8_from 에 따르면 오히려 .
코스

1
getattr(x, 'from')속성 이름을 바꾸는 대신 사용할 수 있습니다 .
조지 V. 라일리

3
이 "솔루션"의 대부분 (심지어 허용 한 중첩 허용하지 않는 일을하지 않는 것 d1.b.c), 나는 당신이 예를 들어, 라이브러리에서 무언가를 사용해야 취소 생각 컬렉션에서 namedtuple 으로, 이 대답은 , 제안 .. .
앤디 헤이든

답변:


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
여기에서도 마찬가지입니다. 이것은 MongoDB와 같은 문서 지향 데이터베이스에서 Python 객체를 재구성 할 때 특히 유용합니다.
mikemaccana

13
더 예쁘게 인쇄하려면 def repr __ (self) : return '<% s>'% str ( '\ n'.join ( '% s : % s'% (k, repr (v)) for (k, v) ) in self .__ dict .iteritems ()))
six8

15
중첩 된 사전에서 작동합니까? 개체 및 / 또는 목록 등을 포함하는 dicts. 캐치가 있습니까?
Sam Stoelinga

5
@ Sam S : Struct중첩 된 사전에서 중첩 된을 만들지 않지만 일반적으로 값의 유형은 무엇이든 될 수 있습니다. 키는 객체 슬롯에 적합하도록 제한됩니다
Eli Bendersky

51
-1 a) 질문이 명확하게 요청한 중첩 된 dicts에서는 작동하지 않으며 b) 동일한 기능이 현재 표준 라이브러리 argparse.Namespace( __eq__,, 에도 정의 __ne__되어 있음 __contains__)에 존재하기 때문입니다.
wim

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 ()로 바꿉니다.
Eric O Lebigot

7
OP 요구 사항이 아닌 경우 이는 문제가되지 않지만 목록 내의 목록에서 개체를 재귀 적으로 처리하지는 않습니다.
Anon

3
나는이 오래된 대답 실현,하지만 더 나은 것 요즘은 사용하는 추상 기본 클래스를 그 추악한 라인 대신에if isinstance(b, (list, tuple)):
WIM

4
팁 : 읽기 가능한 문자열 표현과 같은 추가 기능 objargparse.Namespace위해 클래스를 상속 받으십시오 .
Serrano

2
유스 케이스에 따라, 일반적으로 우리는인지 확인 것 없는 문자열 유형이 있지만한다는 것입니다 시퀀스 유형

107

놀랍게도 아무도 Bunch에 대해 언급하지 않았습니다 . 이 라이브러리는 독점적으로 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 로 이동합니다


16
: 그리고 파이썬 3 호환이 (것으로 보인다 2.6-3.4) 무리의 분기 뭉크라는 이름의 github.com/Infinidat/munch
codyzu

Bunch는 여러 유형의 직렬화를 잘 지원하고 유지 관리되므로 이들 모두에 대한 최상의 솔루션입니다. 감사합니다 python3 버전의 이름이 같은 것이기를 바랍니다.
Jonathan

4
따라서 예고, Bunch 및 Attrdict는 매우 느립니다. 그들은 우리의 응용 프로그램에서 자주 사용되는 것을 보았을 때 각각 내 요청 시간의 약 1/3과 1/2을 소비했습니다. 확실히 무시할 것이 아닙니다. 그것에 대해 더 이야기하십시오 stackoverflow.com/a/31569634/364604 .
JayD3e

이것은 객체와 같은 dicts 액세스를 허용하지만 getattr을 통해 액세스합니다 . 이로 인해 IPython과 같은 스마트 REPL의 내부 검사가 어려워집니다.
GDorn


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
"리프 (leaf)"데이터에는 좋지만, 예제에서는 편리하게 " xx.b<class '__main__.new'>
트위그 (twig)

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
이것은 중첩 된 dicts에 대한 재귀 문제에 대한 답변이 아닙니다.
derigible

4
명명 된 튜플은 수정할 수 없습니다.
최대

keys ()와 values ​​()에 의해 반환 된리스트의 순서가 순서대로 일치한다고 보장 할 수 있습니까? 그것이 OrderedDict라면, 그렇습니다. 그러나 표준 dict? "이 새로운 구현의 순서 보존 측면 구현 세부 간주되고 의존해서는 안된다"나는 CPython과 3.6 및 PyPy "컴팩트"dicts를 주문하지만, 문서를 인용하는 것을 알고있다
하복 (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

모든 깊이의 시퀀스 / dict / value 구조와 함께 사용할 수 있습니다.


4
이것이 답이되어야합니다. 중첩에 효과적입니다. 이것을 json.load ()에 대한 object_hook로 사용할 수도 있습니다.
010110110101

2
2009 년 SilentGhost의 기능 답변과 유사합니다. 리프 노드 데이터에는 액세스 할 수 있지만 부모 / 견상은 개체 참조로 표시됩니다. 예쁘게 인쇄하려면def __repr__(self): return '{%s}' % str(', '.join("'%s': %s" % (k, repr(v)) for (k, v) in self.__dict__.iteritems()))
MarkHu

5
파이썬 3.x 사용자 : 4 번 라인 .items()대신에 사용 .iteritems()되었습니다. (함수 이름이 바뀌었지만 본질적으로 동일합니다. )
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
나는 오히려 내 자신의 대답을 downvote하고 싶지는 않지만 이것을 되돌아 보면 시퀀스 유형으로 되풀이되지 않는다는 것을 알았습니다. 이 경우 xd [1] .foo가 실패합니다.
andyvanee 2013

2
isinstance(v, dict)검사가 더 잘 될 것이다 isinstance(v, collections.Mapping)는 것을 DICT-처럼 미래를 처리 할 수 있도록
호브

29

dict이에서 오는 경우 json.loads()한 줄로 dict 대신 dict 대신 객체로 설정할 수 있습니다.

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 키를 객체 (또는 어려운 키에 대한 dict)로 액세스하려면 재귀 적으로 수행하고 원래 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을 소비했습니다 (what !?). 이 코드는 그 시간을 거의 아무것도 (0.5ms 범위) 줄였습니다. 물론 이것은 당신의 필요에 달려 있습니다.

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) 잘해야합니다.


답변 주셔서 감사합니다,하지만 x는 무엇입니까? dict 또는 표준 개체? 힌트 좀주세요
Marc

x는 객체입니다. 모든 객체에는 dict가 있습니다. 객체 의 받아쓰기 를 업데이트하면 실제로 객체의 주요 변수를 업데이트하게됩니다.
Alex Rodrigues

대담한 단어는 _ dict _ _
Alex Rodrigues

15
중첩 된 사전은 처리하지 않습니다.
FogleBird

모든 물건이 __dict__: 시도 object().__dict__하고 당신이 얻을 수 있는 것은 아닙니다AttributeError: 'object' object has no attribute '__dict__'
Will Manley

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
지금까지 가장 좋은 답변
Connor

중첩 요소에 json 로딩 메커니즘을 사용하는 아이디어가 마음에 듭니다. 그러나 우리는 이미 dict을 가지고 있으며 문자열을 객체로 매핑하기 위해 문자열을 만들어야한다는 사실이 마음에 들지 않습니다. 차라리 dict에서 직접 객체를 만드는 솔루션을 원합니다.
산드로

1
요구는 첫 번째 문자열로 변환하지만 꽤 일반적인 하나는 통해 그것을 확장 할 수 json.JSONEncoderobject_hook.
Sandro

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

아직 목록에서 작동하지 않습니다. __getitem__dicts를 감싸려면 UserList에서 목록 을 감싸고 과부하 해야합니다.


2
목록에서 작동하게하려면 의 답변 if isinstance(d, list)에서 절을 사용하십시오 Anon.
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
오리 타이핑 원리로 인해 객체와 유사합니다. 내 말은 클래스의 인스턴스 인 경우처럼 속성에 액세스 할 수 있다는 것입니다. 그러나 그것은 그런 의미에서 객체가 아니며, 명명 된 튜플입니다.
Aidan Haddon-Wright

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

"from"은 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
Struct 클래스 : def init __ (self, ** entries) : self .__ dict .update (entries)
Kenneth Reitz 2016 년

4

오래된 Q & A이지만 더 이야기 할 것이 있습니다. 재귀 dict에 대해 아무도 이야기하지 않는 것 같습니다. 이것은 내 코드입니다.

#!/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)

클래스로 가져온 유형의 속성을 유지합니다. 내 유일한 관심사는 구문 분석 사전에서 메서드를 덮어 쓰는 것입니다. 그러나 그렇지 않으면 견실 한 것 같습니다!


좋은 생각이지만 OP의 예에서는 효과가없는 것 같습니다. 사실 전달 된 사전을 수정하는 것 같습니다! 사실, df.a심지어 작동하지 않습니다.
Andy Hayden

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

나는 dicts 목록을 객체 목록으로 재귀 적으로 변환 해야하는 경우를 우연히 발견 했으므로 여기에서 Roberto의 발췌 부분을 기반으로 저에게 도움이 된 부분은 다음과 같습니다.

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

물론 dict을 재귀 적으로 걸지 않으면 중첩 된 dict 예제에서 실패합니다.

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

[편집] 다른 dicts뿐만 아니라 list 내의 dicts도 처리하는 것에 대해 놓쳤습니다. 수정 사항이 추가되었습니다.


dict 를 소스 사전으로 설정 하면 결과 객체의 속성을 변경하면 객체를 만든 사전에도 영향을 미치며 그 반대도 마찬가지입니다. 사전을 객체 작성 이외의 용도로 사용하면 예기치 않은 결과가 발생할 수 있습니다.
Mark Roddy

@Mark : 사실, 동일한 사전 개체를 통과하는 대신 매번 새로운 사전이 DictObj로 전달되므로 실제로는 발생하지 않습니다. 사전 내 에서 을 번역해야하기 때문에이 작업을 수행해야 하므로 원본 dict 객체를 직접 변경하지 않고 통과 할 수 없습니다.
Brian

이것에 대한 답변이 꽤 있지만 받아 들인 답변은 재귀 적이거나 목록을 처리하는 것처럼 보이지 않습니다. 나는 그것들을 모두 읽었고 이것은 처음 보았을 때 가장 단순 해 보였고 테스트를 거쳐 효과가있었습니다. 좋은 대답입니다.
AlwaysTraining

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

dict는 숫자, 문자열 및 dict로 구성되어 있다고 생각합니다. 따라서 튜플, 목록 및 기타 유형이 dict의 최종 차원에 나타나지 않는 상황을 무시합니다.

재귀와 결합 된 상속을 고려하면 인쇄 문제를 편리하게 해결하고 데이터를 쿼리하는 두 가지 방법 (데이터 편집 방법)을 제공합니다.

학생에 대한 정보를 설명하는 아래 예를 참조하십시오.

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

일반적으로 dict 계층을 객체에 미러링하지만 일반적으로 가장 낮은 수준의 목록이나 튜플은 아닙니다. 그래서 이것이 내가 한 방법입니다.

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 [ 'hi', 'bar']


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가 lwpb.codec를 사용하여 Python dict로 변환 된 프로토콜 버퍼 데이터에서 디코딩 되었기 때문입니다. 선택적 필드 (예 : 이메일 => 키를 사용할 수없는 경우)와 반복 필드 (예 : phone => dict 목록으로 변환)가 있습니다.

위의 모든 제안 된 솔루션을 시도했습니다. 일부는 중첩 된 사전을 제대로 처리하지 못합니다. 다른 사람은 개체 세부 정보를 쉽게 인쇄 할 수 없습니다.

Dawie Strauss의 dict2obj (dict) 솔루션 만 가장 효과적입니다.

키를 찾을 수 없을 때 처리하기 위해 약간 향상했습니다.

# 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당신이 입력으로 기대에 따라, 그리고 데이터 오브젝트를 뱉어.

노트:

  • trydata더 많은 기능이 필요한 경우 elifs를 추가 할 수 있습니다 .
  • 당신이 원 x.a = {}하거나 비슷한 경우 분명히 작동하지 않습니다 .
  • 읽기 전용 버전을 원하면 원래 답변 의 클래스 데이터를 사용하십시오 .
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.