Konversi dict Python bersarang ke objek?

539

Saya sedang mencari cara yang elegan untuk mendapatkan data menggunakan akses atribut pada dict dengan beberapa dict dan daftar bersarang (yaitu sintaks objek gaya javascript).

Sebagai contoh:

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

Harus dapat diakses dengan cara ini:

>>> x = dict2obj(d)
>>> x.a
1
>>> x.b.c
2
>>> x.d[1].foo
bar

Saya pikir, ini tidak mungkin tanpa rekursi, tetapi apa cara yang bagus untuk mendapatkan gaya objek untuk dikte?

Marc
sumber
6
Saya mencoba melakukan sesuatu yang serupa baru-baru ini, tetapi kunci kamus berulang ("dari" - yang merupakan kata kunci Python) mencegah saya untuk melewatinya. Karena begitu Anda mencoba menggunakan "x.from" untuk mengakses atribut itu, Anda akan mendapatkan kesalahan sintaksis.
Dawie Strauss
3
itu memang masalah, tetapi saya dapat mengabaikan "dari" untuk membuat hidup lebih mudah dalam mengakses konstruksi dikte besar :) mengetik x ['a'] ['d'] [1] ['foo'] benar-benar menjengkelkan, jadi xad [1]. Aturan main. jika Anda memerlukan dari, Anda dapat mengaksesnya melalui getattr (x, 'from') atau menggunakan _from sebagai atribut.
Marc
5
from_daripada _frommenurut PEP 8 .
Kos
1
Anda dapat menggunakan getattr(x, 'from')alih-alih mengganti nama atribut.
George V. Reilly
3
Sebagian besar "solusi" ini tampaknya tidak berfungsi (bahkan yang diterima, tidak mengizinkan bersarang d1.b.c), saya pikir jelas Anda harus menggunakan sesuatu dari perpustakaan, mis. Namupilih dari koleksi , seperti jawaban ini menyarankan, .. .
Andy Hayden

Jawaban:

660

Pembaruan: Dalam Python 2.6 dan seterusnya, pertimbangkan apakah namedtuplestruktur data sesuai dengan kebutuhan Anda:

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

Alternatif (isi jawaban asli) adalah:

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

Kemudian, Anda dapat menggunakan:

>>> args = {'a': 1, 'b': 2}
>>> s = Struct(**args)
>>> s
<__main__.Struct instance at 0x01D6A738>
>>> s.a
1
>>> s.b
2
Eli Bendersky
sumber
19
Sama di sini - ini sangat berguna untuk merekonstruksi objek Python dari database berorientasi dokumen seperti MongoDB.
mikemaccana
13
Untuk mendapatkan pencetakan yang lebih cantik, tambahkan: def repr __ (self): return '<% s>'% str ('\ n' .join ('% s:% s'% (k, repr (v)) untuk (k, v ) dalam diri .__ dict .iteritems ()))
six8
16
Apakah ini akan berfungsi dengan kamus bersarang? dan dicts yang berisi objek dan atau daftar dll. Apakah ada tangkapan?
Sam Stoelinga
5
@ Sam S: itu tidak akan membuat bersarang Structdari kamus bersarang, tetapi secara umum jenis nilai bisa apa saja. Kunci dibatasi agar sesuai untuk slot objek
Eli Bendersky
52
-1 karena) tidak bekerja untuk dicts bersarang, yang pertanyaan dengan jelas meminta, dan b) fungsi yang sama saat ini ada di perpustakaan standar di argparse.Namespace(yang juga telah ditetapkan __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'
Nadia Alramli
sumber
7
Baik! Saya akan mengganti .items () dengan .iteritems (), untuk footprint memori yang lebih kecil.
Eric O Lebigot
7
Jika bukan persyaratan OP, ini bukan masalah - tetapi perhatikan bahwa ini tidak akan memproses objek secara berulang dalam daftar dalam daftar.
Anon
3
Saya menyadari ini adalah jawaban lama, tetapi hari ini akan lebih baik menggunakan kelas dasar abstrak daripada garis jelekif isinstance(b, (list, tuple)):
wim
4
Kiat: Mintalah objkelas mewarisi dari argparse.Namespaceuntuk fitur tambahan seperti representasi string yang dapat dibaca.
Serrano
2
Bergantung pada use case, biasanya kita akan memeriksa bahwa itu bukan tipe string tetapi itu adalah tipe Sequence
wim
108

Anehnya, belum ada yang menyebut Bunch . Perpustakaan ini secara eksklusif dimaksudkan untuk menyediakan akses gaya atribut ke objek-objek dict dan melakukan persis apa yang diinginkan OP. Demonstrasi:

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

Pustaka Python 3 tersedia di https://github.com/Infinidat/munch - Kredit diberikan ke codyzu

kontinuitas
sumber
16
Dan ada cabang python 3 yang kompatibel (tampaknya 2.6-3.4) dari Bunch bernama Munch: github.com/Infinidat/munch
codyzu
Bunch adalah solusi terbaik dari semua ini karena mendukung banyak jenis serialisasi dengan baik, dan dipertahankan. Terima kasih banyak. Saya berharap versi python3 bernama hal yang sama, karena itu mengapa tidak.
Jonathan
4
Jadi hanya peringatan, Bunch dan Attrdict juga dalam hal ini sangat lambat. Mereka menghabiskan sekitar 1/3 dan 1/2 dari waktu permintaan saya masing-masing ketika mereka melihat sering digunakan dalam aplikasi kita. Jelas bukan sesuatu untuk diabaikan. Bicara lebih banyak tentang itu stackoverflow.com/a/31569634/364604 .
JayD3e
Meskipun ini memungkinkan akses seperti objek ke dicts, ia melakukannya melalui getattr . Ini membuat introspeksi dalam REPL pintar seperti IPython sulit.
GDorn
Penasaran bagaimana ini dibandingkan dengan JSONpath github.com/kennknowles/python-jsonpath-rw
MarkHu
64
x = type('new_dict', (object,), d)

kemudian tambahkan rekursi untuk ini dan Anda selesai.

edit ini bagaimana saya menerapkannya:

>>> 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'
SilentGhost
sumber
1
Mengapa Anda membuat objek tipe dan tidak membuat instance mereka? Bukankah itu lebih logis? Maksud saya, mengapa tidak melakukannya top_instance = top()dan mengembalikannya ke tempat Anda kembali top?
pancake
6
Bagus untuk data "daun", tetapi contoh-contohnya dengan mudah meninggalkan "ranting" seperti xdan x.byang kembali jelek<class '__main__.new'>
MarkHu
46

Ada penolong koleksi bernama namedtuple, yang dapat melakukan ini untuk Anda:

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
umbrae
sumber
28
Ini tidak menjawab pertanyaan rekursi untuk dikte bersarang.
memenuhi syarat
4
Anda tidak dapat mengubah namadupel.
Maks
Bisakah kami menjamin bahwa urutan daftar dikembalikan oleh kunci () dan nilai () sesuai dengan urutan? Maksudku, jika itu adalah OrderedDict, ya. Tapi dict standar? Saya tahu bahwa perintah CPython 3.6+ dan PyPy "compact" dipesan, tetapi mengutip dokumentasi: "Aspek pelestarian pesanan dari implementasi baru ini dianggap sebagai detail implementasi dan tidak boleh diandalkan"
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

Dapat digunakan dengan urutan / dikt / struktur nilai kedalaman apa pun.

Xye
sumber
4
Ini seharusnya jawabannya. Ini bekerja dengan baik untuk bersarang. Anda dapat menggunakan ini sebagai object_hook untuk json.load () juga.
010110110101
2
Mirip dengan jawaban fungsional SilentGhost dari 2009 - data node daun dapat diakses, tetapi induk / ranting ditampilkan sebagai referensi objek. Untuk mencetak cantik,def __repr__(self): return '{%s}' % str(', '.join("'%s': %s" % (k, repr(v)) for (k, v) in self.__dict__.iteritems()))
MarkHu
5
Pengguna Python 3.x: hanya saja .items()bukan .iteritems()di baris 4. (Fungsi ini diganti namanya, tetapi tidak sama )
neo post modern
Berfungsi sempurna untuk objek bersarang, terima kasih! Sebagai komentar, pemula seperti saya, untuk python3 iteritems () harus diubah menjadi item ()
Óscar Andreu
31

Mengambil apa yang saya rasakan adalah aspek terbaik dari contoh-contoh sebelumnya, inilah yang saya buat:

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()))
andyvanee
sumber
Perhatikan bahwa konstruktor dapat disingkat menjadi: def __init__(self, dct): for k, v in dct.iteritems(): setattr(self, k, isinstance(v, dict) and self.__class__(v) or v) yang juga menghilangkan panggilan eksplisit keStruct
George V. Reilly
2
Saya lebih suka tidak meremehkan jawaban saya sendiri, tetapi melihat kembali ini saya perhatikan bahwa itu tidak muncul kembali menjadi jenis urutan. xd [1] .foo gagal dalam kasus ini.
andyvanee
2
yang isinstance(v, dict)cek akan lebih baik sebagai isinstance(v, collections.Mapping)sehingga dapat menangani masa depan dict-seperti hal
kompor
30

Jika dict Anda berasal json.loads(), Anda dapat mengubahnya menjadi objek alih-alih (bukan dict) dalam satu baris:

import json
from collections import namedtuple

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

Lihat juga Cara mengonversi data JSON menjadi objek Python .

DS.
sumber
Tampaknya jauh lebih lambat dari biasanya .loadsjika Anda memiliki objek JSON besar
michaelsnowden
16

Jika Anda ingin mengakses kunci dict sebagai objek (atau sebagai dict untuk kunci yang sulit), lakukan secara rekursif, dan juga dapat memperbarui dict yang asli, Anda dapat melakukan:

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)

Contoh penggunaan:

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'}}
pengguna1783597
sumber
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'
Segera
sumber
12

Saya akhirnya mencoba KEDUA AttrDict dan Bunchperpustakaan dan menemukan mereka terlalu lambat untuk saya gunakan. Setelah seorang teman dan saya melihatnya, kami menemukan bahwa metode utama untuk menulis perpustakaan ini menghasilkan perpustakaan secara agresif berulang melalui objek bersarang dan membuat salinan dari objek kamus di seluruh. Dengan mengingat hal ini, kami membuat dua perubahan penting. 1) Kami membuat atribut lazy-loaded 2) alih-alih membuat salinan objek kamus, kami membuat salinan objek proxy yang ringan. Ini adalah implementasi terakhir. Peningkatan kinerja menggunakan kode ini luar biasa. Saat menggunakan AttrDict atau Bunch, dua perpustakaan ini saja menghabiskan 1/2 dan 1/3 masing-masing dari waktu permintaan saya (apa !?). Kode ini mengurangi waktu itu menjadi hampir tidak ada (di suatu tempat di kisaran 0,5 ms). Ini tentu saja tergantung pada kebutuhan Anda,

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

Lihat implementasi asli di sini oleh https://stackoverflow.com/users/704327/michael-merickel .

Hal lain yang perlu diperhatikan, adalah implementasi ini cukup sederhana dan tidak menerapkan semua metode yang mungkin Anda butuhkan. Anda harus menulis yang diperlukan pada objek DictProxy atau ListProxy.

JayD3e
sumber
9

x.__dict__.update(d) harus baik-baik saja.

Alex Rodrigues
sumber
terima kasih atas jawaban Anda, tetapi apa itu x? dict atau objek standar? bisa tolong beri saya petunjuk.
Marc
x adalah objek Anda. Setiap objek memiliki dict . Dengan memperbarui dikt objek Anda benar-benar memperbarui vars kunci di dalamnya.
Alex Rodrigues
Kata-kata yang berani adalah _ _ dict _ _
Alex Rodrigues
15
Ini tidak akan menangani kamus bersarang.
FogleBird
Tidak setiap objek memiliki __dict__: coba object().__dict__dan Anda akan dapatkanAttributeError: 'object' object has no attribute '__dict__'
Will Manley
8

Anda dapat memanfaatkan jsonmodul perpustakaan standar dengan kait objek khusus :

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)

Contoh penggunaan:

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

Dan tidak sepenuhnya hanya-baca seperti itu namedtuple, yaitu Anda dapat mengubah nilai - bukan struktur:

>>> o.b.c = 3
>>> o.b.c
3
Vanni Totaro
sumber
1
Jawaban terbaik sejauh ini
Connor
Saya suka ide untuk menggunakan mekanisme memuat json untuk elemen bersarang. Tapi kami sudah punya dict dan saya tidak suka fakta yang perlu membuat string untuk memetakannya menjadi objek. Saya lebih suka memiliki solusi yang membuat objek langsung dari dict.
Sandro
1
Perlu dikonversi ke string terlebih dahulu tetapi cukup generik karena dapat diperluas melalui json.JSONEncoderdan object_hook.
Sandro
6

Ini harus Anda mulai:

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

Belum berfungsi untuk daftar. Anda harus membungkus daftar dalam Daftar Pengguna dan membebani __getitem__untuk membungkus dicts.

Aaron Digulla
sumber
2
Untuk membuatnya berfungsi untuk daftar, gunakan if isinstance(d, list)klausa dari Anonjawaban.
Vinay Sajip
6

Saya tahu sudah ada banyak jawaban di sini sudah dan saya terlambat ke pesta tetapi metode ini akan secara rekursif dan 'di tempat' mengkonversi kamus ke struktur seperti objek ... Bekerja di 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
Aidan Haddon-Wright
sumber
Apa maksudmu "struktur seperti objek"?
MoralCode
1
Seperti objek karena prinsip mengetik bebek. Maksud saya adalah Anda dapat mengakses propertinya seperti yang Anda lakukan jika itu adalah turunan dari sebuah kelas. tapi itu BUKAN objek dalam arti itu, itu adalah tuple bernama.
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
meneruskan
sumber
4

Biarkan saya menjelaskan solusi yang saya hampir gunakan beberapa waktu lalu. Tapi pertama-tama, alasan saya tidak diilustrasikan oleh fakta bahwa kode berikut:

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

print x.from

memberikan kesalahan ini:

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

Karena "dari" adalah kata kunci Python, ada kunci kamus tertentu yang tidak dapat Anda izinkan.


Sekarang solusi saya memungkinkan akses ke item kamus dengan menggunakan nama mereka secara langsung. Tetapi juga memungkinkan Anda untuk menggunakan "semantik kamus". Berikut adalah kode dengan contoh penggunaan:

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"
Dawie Strauss
sumber
1
class Struct: def init __ (entri mandiri, **): self .__ dict .update (entri)
Kenneth Reitz
4

Tanya Jawab Lama, tapi saya ingin bicara lebih banyak. Sepertinya tidak ada yang bicara tentang dict rekursif. Ini kode saya:

#!/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()
truease.com
sumber
4

Ingin mengunggah versi saya dari paradigma kecil ini.

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)

Ini mempertahankan atribut untuk tipe yang diimpor ke dalam kelas. Satu-satunya kekhawatiran saya adalah menimpa metode dari dalam kamus parsing Anda. Tetapi sebaliknya terlihat solid!

Erik
sumber
Meskipun ide yang bagus, ini tampaknya tidak berfungsi untuk contoh OP, bahkan tampaknya memodifikasi kamus yang diteruskan! Bahkan, df.atidak berfungsi.
Andy Hayden
4

Ini juga berfungsi dengan baik

class DObj(object):
    pass

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

print dobj.a
>>> aaa
print dobj.b
>>> bbb
naren
sumber
3

Berikut ini cara lain untuk mengimplementasikan saran asli 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
rampok
sumber
3

Saya menemukan case yang saya butuhkan untuk secara rekursif mengubah daftar dicts ke daftar objek, jadi berdasarkan potongan Roberto di sini apa yang berhasil bagi saya:

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

Perhatikan bahwa setiap tuple akan dikonversi ke daftar yang setara, karena alasan yang jelas.

Semoga ini bisa membantu seseorang seperti halnya semua jawaban Anda untuk saya, kawan.

NiKo
sumber
3

Bagaimana dengan hanya menempatkan Anda dictke __dict__objek kosong?

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

Tentu saja ini gagal pada contoh dict Anda kecuali jika Anda menjalankan dict tersebut secara rekursif:

# 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

Dan elemen daftar contoh Anda mungkin dimaksudkan untuk menjadi Mapping, daftar pasangan (kunci, nilai) seperti ini:

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

Inilah implementasi lain:

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

[Sunting] Melewatkan sedikit tentang juga menangani dicts dalam daftar, bukan hanya dicts lainnya. Menambahkan perbaikan.

Brian
sumber
Perhatikan bahwa pengaturan dict ke kamus sumber berarti bahwa setiap perubahan atribut pada objek yang dihasilkan juga akan mempengaruhi kamus yang membuat objek, dan sebaliknya. Ini dapat menyebabkan hasil yang tidak terduga jika kamus digunakan untuk sesuatu selain membuat objek.
Mark Roddy
@ Mark: Sebenarnya, kamus baru sedang dikirimkan ke DictObj setiap kali, daripada hanya melewati objek dict yang sama, jadi ini tidak akan benar-benar terjadi. Ini perlu untuk melakukan ini, karena saya perlu menerjemahkan nilai - nilai dalam kamus juga, sehingga tidak mungkin untuk melewati objek dict asli tanpa memutasikannya sendiri.
Brian
Ada beberapa jawaban untuk ini, jawaban yang diterima sepertinya tidak bersifat rekursif atau menangani daftar. Saya membaca semuanya dan yang ini terlihat paling sederhana pada pandangan pertama, saya diuji dan berhasil. Jawaban yang bagus
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))

Pemakaian:

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
Paulo Freitas
sumber
2

Bagaimana dengan ini:

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

Ini kemudian dapat digunakan seperti ini:

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

Saya pikir sebuah dikt terdiri dari angka, string, dan dikt cukup banyak waktu. Jadi saya mengabaikan situasi yang tupel, daftar, dan jenis lainnya yang tidak muncul dalam dimensi akhir suatu dikt.

Mempertimbangkan pewarisan, dikombinasikan dengan rekursi, ia menyelesaikan masalah pencetakan dengan mudah dan juga menyediakan dua cara untuk meminta data, satu cara untuk mengedit data.

Lihat contoh di bawah ini, sebuah dikt yang menggambarkan beberapa informasi tentang siswa:

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)

Hasil:

{'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': ''}
tcpiper
sumber
2

Biasanya Anda ingin mencerminkan hierarki dikt ke dalam objek Anda tetapi tidak daftar atau tupel yang biasanya di tingkat terendah. Jadi ini adalah bagaimana saya melakukan ini:

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)

Jadi kami lakukan:

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

dan dapatkan:

x.b.b1.x 1

x.c ['hai', 'bar']

VengaVenga
sumber
1

Kamus saya dalam format ini:

addr_bk = {
    'person': [
        {'name': 'Andrew', 'id': 123, 'email': '[email protected]',
         'phone': [{'type': 2, 'number': '633311122'},
                   {'type': 0, 'number': '97788665'}]
        },
        {'name': 'Tom', 'id': 456,
         'phone': [{'type': 0, 'number': '91122334'}]}, 
        {'name': 'Jack', 'id': 7788, 'email': '[email protected]'}
    ]
}

Seperti dapat dilihat, saya memiliki kamus dan daftar dikte bersarang . Ini karena addr_bk didekodekan dari data buffer protokol yang dikonversi ke python dict menggunakan lwpb.codec. Ada bidang opsional (mis. Email => di mana kunci mungkin tidak tersedia) dan bidang berulang (mis. Telepon => dikonversi ke daftar dict).

Saya mencoba semua solusi yang diusulkan di atas. Beberapa tidak menangani kamus bersarang dengan baik. Orang lain tidak dapat mencetak detail objek dengan mudah.

Hanya solusinya, dict2obj (dict) oleh Dawie Strauss, yang paling berhasil.

Saya telah sedikit meningkatkannya untuk menangani ketika kunci tidak dapat ditemukan:

# 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

Lalu, saya mengujinya dengan:

# 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
Whospal
sumber
1

Membangun jawaban saya untuk " python: Bagaimana cara menambahkan properti ke kelas secara dinamis? ":

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

Anda memanggil makedatakamus yang ingin Anda konversi, atau mungkin trydatatergantung pada apa yang Anda harapkan sebagai input, dan itu memuntahkan objek data.

Catatan:

  • Anda dapat menambahkan elif trydatajika Anda membutuhkan lebih banyak fungsi.
  • Jelas ini tidak akan berfungsi jika Anda mau x.a = {}atau serupa.
  • Jika Anda ingin versi hanya baca, gunakan data kelas dari jawaban asli .
David X
sumber