ネストされたPython dictをオブジェクトに変換しますか?


538

私はいくつかのネストされたdictとリスト(つまり、javascriptスタイルのオブジェクト構文)を持つdictの属性アクセスを使用してデータを取得するエレガントな方法を探しています。

例えば:

>>> 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
最近、似たようなことをしようとしていましたが、繰り返し発生する辞書キー(Pythonのキーワードである "from")が原因で、それを実行できませんでした。「x.from」を使用してその属性にアクセスしようとすると、構文エラーが発生するためです。
Dawie Strauss、

3
それは確かに問題ですが、「from」を放棄して、大きなdict構文に簡単にアクセスできるようにすることができます:) x ['a'] ['d'] [1] ['foo']と入力すると、本当に煩わしいので、xad [1] .fooルール。fromが必要な場合は、getattr(x、 'from')を介してアクセスするか、代わりに属性として_fromを使用できます。
マルク

5
from_PEP 8_fromによるのではなく。
Kos

1
getattr(x, 'from')属性の名前を変更する代わりに使用できます。
ジョージV.ライリー

3
これらのほとんどは、「ソリューション」は、(ネストされたことはできません、でも受け入れられた1つの作業にいないようですd1.b.c)、私はそれはあなたが例えば、ライブラリーから何かを使用する必要があります明らかだと思うのコレクションからnamedtupleとして、この答えは示唆しています.. 。
アンディ・ヘイデン

回答:


660

更新: 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 2010年

13
よりきれいに印刷するには、次を追加します:def repr __(self):return '<%s>'%str( '\ n' .join( '%s:%s'%(k、repr(v))for(k、v )in self .__ dict .iteritems()))
six8

15
これはネストされた辞書で機能しますか?オブジェクトやリストなどを含む辞書。キャッチはありますか?
Sam Stoelinga

5
@Sam S:Structネストされた辞書からネストされたsを作成しませんが、一般的に値のタイプは何でもかまいません。キーはオブジェクトスロットに適したものに制限されています
Eli Bendersky

51
-1、それは質問を明確に同じ機能が、現在では標準ライブラリに存在を求め、およびb)ネストされた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()に置き換えて、メモリフットプリントを小さくします。
エリックOレビゴット2009

7
OP要件ではない場合、これは問題ではありませんが、リスト内のリスト内のオブジェクトを再帰的に処理しないことに注意してください。
Anon、

3
これは古い答えだと思いますが、最近では、醜い線ではなく抽象基本クラスを使用する方がよいでしょうif isinstance(b, (list, tuple)):
wim

4
ヒント:読み取り可能な文字列表現などの追加機能のために、objクラスが継承するargparse.Namespaceようにします。
セラーノ

2
ユースケースによっては、通常、我々はそれがあることを確認しますない文字列型が、それはということであるシーケンスタイプ
WIM

108

驚くべきことに、誰も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
そして、Munchという名前のBunchのpython 3互換(2.6-3.4と思われる)ブランチがあります:github.com/Infinidat/munch
codyzu

Bunchは、複数のタイプのシリアライゼーションを適切にサポートし、維持されているため、これらすべての最良のソリューションです。よかった。ありがとう。私はpython3バージョンが同じ名前になっていることを望みます。
ジョナサン

4
ですから、事前に注意しておくと、BunchとAttrdictも同様に非常に遅いです。私たちのアプリケーションで頻繁に使用されているのを見たとき、彼らはそれぞれ私のリクエスト時間の約1/3と1/2を消費しました。間違いなく無視するものではありません。詳細については、stackoverflow.com / a / 31569634/364604をご覧ください。
JayD3e 2015

これはdictsへのオブジェクトのようなアクセスを許可しますが、それは経由そうGETATTR。これにより、IPythonのようなスマートREPLの内省が困難になります。
GDorn 2015

これは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ですか?
2014年

6
「葉」のデータにはいいのですが、例では、「小枝」のようなものxx.b<class '__main__.new'>
省略し、

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
これは、入れ子になった辞書の再帰の問題には答えません。
2013年

4
名前付きタプルは変更できません。
最大

keys()とvalues()によって返されるリストの順序が順序どおりに一致することを保証できますか?つまり、それがOrderedDictであれば、はい。しかし、標準的な口述?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

任意の深さの任意のシーケンス/辞書/値の構造で使用できます。


4
これが答えになるはずです。ネストに適しています。これをjson.load()のobject_hookとしても使用できます。
010110110101 2014

2
2009年のSilentGhostの機能的な回答に似ています-リーフノードデータにアクセスできますが、親/小枝はオブジェクト参照として表示されます。きれいに印刷するには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行目に(機能の名前を変更、しかしいた同じessentialy
ネオポストモダン

ネストされたオブジェクトに最適です。ありがとうございます。コメントとして)のpython3のiteritemsのためのI、(など初心者)が(アイテムに変更する必要があります
オスカル・アンドリュー

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
ジョージV.ライリー

2
私は自分の回答に反対票を投じたくありませんが、これを振り返ると、シーケンスタイプに再帰しないことに気づきました。この場合、xd [1] .fooは失敗します。
andyvanee 2013

2
将来のdictのようなものを処理できるように、isinstance(v, dict)チェックはより良いでしょうisinstance(v, collections.Mapping)
ホブ2013


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

AttrDictBunchの両方を試してしまいましたライブラリとそれらが私の用途には遅すぎることがわかりました。友人と私が調べたところ、これらのライブラリを作成するための主な方法の結果、ライブラリはネストされたオブジェクトを積極的に再帰し、辞書オブジェクトのコピーを作成していることがわかりました。これを念頭に置いて、2つの重要な変更を加えました。1)属性を遅延ロードしました2)辞書オブジェクトのコピーを作成する代わりに、軽量プロキシオブジェクトのコピーを作成します。これが最終的な実装です。このコードを使用すると、パフォーマンスが大幅に向上します。AttrDictまたはBunchを使用すると、これらの2つのライブラリーだけで、要求時間のそれぞれ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による元の実装をここで参照してください

もう1つ注意すべき点は、この実装は非常に単純であり、必要なすべてのメソッドを実装していないことです。必要に応じて、DictProxyまたはListProxyオブジェクトにそれらを書き込む必要があります。


9

x.__dict__.update(d) うまくいくはずです。


あなたの答えをありがとう、しかしxは何ですか?辞書または標準オブジェクト?ヒントをください。
マルク

xはオブジェクトです。すべてのオブジェクトにはdictがあります。オブジェクトのdictを更新することで、実際にはその中のキー変数を更新しています。
アレックスロドリゲス

太字の単語は_ _ dict _ _
Alex Rodrigues、

15
これはネストされた辞書を処理しません。
FogleBird 2009

必ずしもすべてのオブジェクトがあり__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ロードメカニズムを使用するというアイデアが気に入っています。しかし、私たちはすでにdictを持っているので、オブジェクトにマップするために文字列を作成する必要があるという事実は好きではありません。辞書から直接オブジェクトを作成するソリューションが欲しいです。
サンドロ

1
最初に文字列に変換する必要がjson.JSONEncoderありobject_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

リストにはまだ対応していません。リストをUserListで__getitem__ラップし、オーバーロードして辞書をラップする必要があります。


2
リストで機能させるには、の回答のif isinstance(d, list)節を使用しAnonます。
Vinay Sajip、2009

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

4

古いQ&Aですが、もう少し話をします。再帰的口述について誰も話していないようです。これは私のコードです:

#!/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は、動作しません。
アンディヘイデン

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

[編集]他の辞書だけでなく、リスト内の辞書も処理することについて少し見落としました。修正を追加しました。


dictをソースディクショナリに設定すると、結果のオブジェクトの属性を変更すると、オブジェクトを作成したディクショナリにも影響があり、その逆も同様です。オブジェクトを作成する以外の目的でディクショナリを使用すると、予期しない結果になる可能性があります。
マークロディ

@Mark:実際には、同じ辞書オブジェクトを単に通過するのではなく、新しい辞書が毎回DictObjに渡されるため、これは実際には発生しません。辞書内の値も変換する必要があるので、これを行う必要があります。そのため、元のdictオブジェクトを自分で変更しないと通過できません。
ブライアン、

これにはかなりの数の回答があり、受け入れられた回答は再帰的またはリストを処理するようには見えませんでした。私はそれらをすべて読みましたが、これは一見すると最も簡単に見えました。テストしてみましたが、うまくいきました。すばらしい答えです。
AlwaysTraining 2019

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の最終次元に表示されない状況は無視します。

継承と再帰を組み合わせることで、印刷の問題を簡単に解決し、データを照会する2つの方法と、データを編集する1つの方法を提供します。

次の例を参照してください。これは、生徒に関するいくつかの情報を記述した辞書です。

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に変換されたプロトコルバッファーデータからデコードされたためです。オプションのフィールド(例:email =>鍵が使用できない場合があります)と繰り返しフィールド(例:phone =>辞書のリストに変換)があります。

上記のすべての解決策を試しました。ネストされた辞書をうまく処理できないものもあります。他の人はオブジェクトの詳細を簡単に印刷できません。

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さらに機能が必要な場合は、elifを追加できます。
  • もちろん、これはあなたが望むx.a = {}か同様の場合には機能しません。
  • 読み取り専用バージョンが必要な場合は、元の回答のクラスデータを使用してください。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.