Cara menggunakan titik "." untuk mengakses anggota kamus?


283

Bagaimana cara membuat anggota kamus Python dapat diakses melalui titik "."?

Misalnya, alih-alih menulis mydict['val'], saya ingin menulis mydict.val.

Juga saya ingin mengakses dicts bersarang dengan cara ini. Sebagai contoh

mydict.mydict2.val 

akan merujuk

mydict = { 'mydict2': { 'val': ... } }

20
Banyak situasi di mana orang menggunakan dicts bersarang akan sama baiknya atau lebih baik dilayani oleh dicts dengan tuple sebagai kunci, di mana d[a][b][c]digantikan oleh d[a, b, c].
Mike Graham

7
Itu bukan sihir: foo = {}; foo [1,2,3] = "satu, dua, tiga!"; foo.keys () => [(1,2,3)]
Bryan Oakley

10
Wow. Wow lagi. Saya tidak tahu tupel bisa menjadi kunci dict. Wow ketiga kalinya.
bodacydo

3
Objek apa pun yang "hashable" dapat digunakan sebagai kunci dict. Sebagian besar objek yang tidak berubah juga dapat di-hash, tetapi hanya jika semua isinya dapat di-hashable. Kode d [1, 2, 3] berfungsi karena "," adalah "buat operator tuple"; sama dengan d [(1, 2, 3)]. Kurung sering opsional di sekitar deklarasi tuple.
Larry Hastings

6
Sudahkah Anda mempertimbangkan kasus di mana kunci memiliki titik dengan sendirinya - {"my.key":"value"}? Atau ketika kuncinya adalah kata kunci, seperti "dari"? Saya telah mempertimbangkannya beberapa kali, dan ini lebih banyak masalah dan pemecahan masalah daripada manfaat yang dirasakan.
Todor Minakov

Jawaban:


147

Anda dapat melakukannya menggunakan kelas yang baru saja saya buat. Dengan kelas ini Anda dapat menggunakan Mapobjek seperti kamus lain (termasuk serialisasi json) atau dengan notasi titik. Saya berharap dapat membantu Anda:

class Map(dict):
    """
    Example:
    m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
    """
    def __init__(self, *args, **kwargs):
        super(Map, self).__init__(*args, **kwargs)
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    self[k] = v

        if kwargs:
            for k, v in kwargs.iteritems():
                self[k] = v

    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(Map, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(Map, self).__delitem__(key)
        del self.__dict__[key]

Contoh penggunaan:

m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
# Add new key
m.new_key = 'Hello world!'
# Or
m['new_key'] = 'Hello world!'
print m.new_key
print m['new_key']
# Update values
m.new_key = 'Yay!'
# Or
m['new_key'] = 'Yay!'
# Delete key
del m.new_key
# Or
del m['new_key']

21
Untuk bekerja pada Python 3 saya memperbarui .iteritems()ke.items()
berto

13
Perhatikan bahwa ini akan berperilaku berbeda dari harapan umum dalam hal itu tidak akan naik AttributeErrorjika atribut tidak ada. Sebaliknya itu akan kembali None.
mic_e

Rekomendasikan menambahkan getstate dan setstate sehingga salinan yang dalam dan sistem lain dapat mendukungnya.
user1363990

4
Anda dapat menyederhanakan konstruktor Anda self.update(*args,**kwargs). Anda juga bisa menambahkan __missing__(self,key): value=self[key]= type(self)(); return value. Kemudian Anda dapat menambahkan entri yang hilang menggunakan notasi titik. Jika Anda ingin dipilih, Anda dapat menambahkan __getstate__dan__setstate__
Jens Munk

1
Ini akan membuat hasattr(Map, 'anystring') is true. which means the hasattr would always return True due to overriding __getattr__`
Xiao

266

Saya selalu menyimpan ini di dalam file util. Anda dapat menggunakannya sebagai mixin di kelas Anda sendiri juga.

class dotdict(dict):
    """dot.notation access to dictionary attributes"""
    __getattr__ = dict.get
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

mydict = {'val':'it works'}
nested_dict = {'val':'nested works too'}
mydict = dotdict(mydict)
mydict.val
# 'it works'

mydict.nested = dotdict(nested_dict)
mydict.nested.val
# 'nested works too'

5
Jawabannya sangat sederhana, hebat! Apakah Anda tahu apa yang perlu saya lakukan agar memiliki penyelesaian tab dalam pekerjaan IPython? Kelas perlu mengimplementasikan __dir __ (mandiri), tetapi entah bagaimana saya tidak bisa membuatnya bekerja.
andreas-h

8
+1 untuk kesederhanaan. tetapi tampaknya tidak bekerja pada dicts bersarang. d = {'foo': {'bar': 'baz'}}; d = dotdict(d); d.foo.barmelempar kesalahan atribut, tetapi d.fooberfungsi dengan baik.
tmthyjames

2
Yap ini tidak berfungsi untuk struktur bersarang yang kompleks.
David

17
@ tmthyjames Anda cukup mengembalikan objek tipe dotdict dalam metode pengambil untuk mengakses atribut secara rekursif dengan notasi titik seperti: python class DotDict(dict): """dot.notation access to dictionary attributes""" def __getattr__(*args): val = dict.get(*args) return DotDict(val) if type(val) is dict else val __setattr__ = dict.__setitem__ __delattr__ = dict.__delitem__
TMKasun

4
Setelah bereksperimen dengan itu, tampaknya getmemang ide yang buruk karena itu akan kembali Nonebukannya meningkatkan kesalahan untuk barang yang hilang ...
NichtJens

117

Instal dotmapviapip

pip install dotmap

Itu melakukan semua yang Anda ingin lakukan dan subclass dict, sehingga beroperasi seperti kamus normal:

from dotmap import DotMap

m = DotMap()
m.hello = 'world'
m.hello
m.hello += '!'
# m.hello and m['hello'] now both return 'world!'
m.val = 5
m.val2 = 'Sam'

Selain itu, Anda dapat mengubahnya ke dan dari dictobjek:

d = m.toDict()
m = DotMap(d) # automatic conversion in constructor

Ini berarti bahwa jika sesuatu yang ingin Anda akses sudah dalam dictbentuk, Anda dapat mengubahnya menjadi DotMapakses yang mudah:

import json
jsonDict = json.loads(text)
data = DotMap(jsonDict)
print data.location.city

Akhirnya, secara otomatis membuat DotMapinstance anak baru sehingga Anda dapat melakukan hal-hal seperti ini:

m = DotMap()
m.people.steve.age = 31

Perbandingan dengan Bunch

Pengungkapan penuh: Saya adalah pencipta DotMap . Saya membuatnya karena Bunchtidak memiliki fitur-fitur ini

  • mengingat item pesanan ditambahkan dan iterasi dalam urutan itu
  • DotMappembuatan anak otomatis , yang menghemat waktu dan membuat kode lebih bersih ketika Anda memiliki banyak hierarki
  • membangun dari dictdan secara rekursif mengkonversi semua dictinstance anakDotMap

2
:-) dapatkah Anda membuatnya bekerja dengan kunci yang sudah ada titik dalam namanya? {"test.foo": "bar"}dapat diakses melalui mymap.test.fooItu akan fantastis. Dibutuhkan beberapa regresi untuk mengubah peta datar menjadi peta yang dalam kemudian menerapkan DotMap ke dalamnya, tetapi itu sangat berharga!
dlite922

Rapi. Adakah cara untuk membuat daftar / penyelesaian tab berfungsi dengan kunci-kunci di notebook Jupyter? Akses dot-style paling berharga untuk penggunaan interaktif.
Dmitri

@Dmitri Produk keren. Belum pernah mendengarnya sebelumnya, jadi saya tidak yakin bagaimana membuat autocomplete berfungsi. Saya setuju menggunakan DotMapdengan autocomplete yang terbaik. Saya menggunakan Teks Sublim, yang secara otomatis melengkapi kata kunci yang diketik sebelumnya.
Chris Redford

1
Saya menemukan itu tidak memiliki ekstraksi kamus untuk hal-hal seperti **kwargsatau c = {**a, **b}. Bahkan, gagal diam-diam, itu berperilaku seperti kamus kosong saat mengekstraksi.
Simon Streicher

@SimonStreicher Saya menguji ini dengan m = DotMap(); m.a = 2; m.b = 3; print('{a} {b}'.format(**m));dan saya mendapatkan yang diharapkan 2 3. Jika Anda memiliki kasus rusak yang terbukti berhasil dict()tetapi tidak DotMap(), kirimkan kode Anda ke tab Masalah di GitHub.
Chris Redford

56

Berasal dari dikt dan dan mengimplementasikan __getattr__dan __setattr__.

Atau Anda dapat menggunakan Bunch yang sangat mirip.

Saya tidak berpikir itu mungkin untuk monkeypatch kelas dict bawaan.


2
Apa arti sebenarnya dari monkeypatch? Saya sudah mendengarnya tetapi tidak digunakan. (Maaf saya mengajukan pertanyaan pemula seperti itu, saya belum begitu mahir dalam pemrograman (saya baru mahasiswa tahun ke-2.))
bodacydo

9
Monkeypatching menggunakan dinamika Python (atau bahasa apa pun) untuk mengubah sesuatu yang biasanya didefinisikan dalam kode sumber. Ini terutama berlaku untuk mengubah definisi kelas setelah mereka dibuat.
Mike Graham

Jika Anda sering menggunakan fungsi ini, waspadalah terhadap kecepatan Bunch. Saya sering menggunakannya dan menghabiskan sepertiga waktu permintaan saya. Lihatlah jawaban saya untuk penjelasan lebih rinci tentang ini.
JayD3e

22

Kain memiliki implementasi yang sangat bagus, minimal . Memperluas itu untuk memungkinkan akses bersarang, kita dapat menggunakan defaultdict, dan hasilnya terlihat seperti ini:

from collections import defaultdict

class AttributeDict(defaultdict):
    def __init__(self):
        super(AttributeDict, self).__init__(AttributeDict)

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

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

Manfaatkan sebagai berikut:

keys = AttributeDict()
keys.abc.xyz.x = 123
keys.abc.xyz.a.b.c = 234

Itu menguraikan sedikit pada jawaban Kugel tentang "Berasal dari dikt dan dan mengimplementasikan __getattr__dan __setattr__". Sekarang kamu tahu caranya!


1
Yang itu luar biasa!
Thomas Klinger

Senang memasukkan defaultdict - namun ini tampaknya hanya bekerja ketika memulai dict dari awal. Jika kita perlu mengkonversi dict yang ada menjadi "dotdict" secara rekursif. Berikut ini alternatif dotdictyang memungkinkan untuk mengonversi dictobjek yang ada secara rekursif: gist.github.com/miku/…
miku

19

Saya mencoba ini:

class dotdict(dict):
    def __getattr__(self, name):
        return self[name]

Anda dapat mencoba __getattribute__ juga.

buat setiap dict jenis dotdict akan cukup bagus, jika Anda ingin init ini dari multi-layer dict, coba implementasikan __init__juga.


oops, jawaban @Kugel serupa.
tdihp

1
tdihp, saya masih menyukai jawaban Anda karena saya memahaminya lebih cepat - ia memiliki kode aktual.
yigal

1
+1 untuk kode aktual. Tapi saran @ Kugel untuk menggunakan Bunch juga sangat bagus.
Dannid

Saya merasa berguna untuk menanamkan ini di dalam suatu fungsi dengan menempatkan def docdict(name):sebelumnya dan kemudian `if isinstance (name, dict): return DotDict (name) return
name`

contoh sederhana yang bagus .. Saya memperpanjang ini sedikit sehingga dict bersarang mudah dirantai, mirip dengan @DanielMoskovich, tetapi juga mengembalikan daun node dengan benar untuk int, string, dll ... atau null jika tidak ditemukanclass dotdict(dict): def __getattr__(self, name): if name not in self: return None elif type(self[name]) is dict: return JsonDot(self[name]) else: return self[name]
D Sievers

11

Jangan. Akses atribut dan pengindeksan adalah hal-hal terpisah dalam Python, dan Anda seharusnya tidak ingin mereka melakukan hal yang sama. Buat kelas (mungkin yang dibuat oleh namedtuple) jika Anda memiliki sesuatu yang harus memiliki atribut yang dapat diakses dan menggunakan []notasi untuk mendapatkan item dari dict.


Terima kasih atas jawabannya. Tetapi lihat pertanyaan ini yang juga baru saja saya tanyakan: stackoverflow.com/questions/2352252/... Ini sepertinya ide yang bagus untuk digunakan .daripada []mengakses struktur data yang rumit dalam templat Mako.
bodacydo

2
Saya bisa melihat use case untuk ini; sebenarnya, saya melakukannya hanya beberapa minggu yang lalu. Dalam kasus saya, saya menginginkan objek yang dapat mengakses atribut dengan notasi titik. Saya merasa sangat mudah untuk hanya mewarisi dari dict jadi saya mendapatkan semua fitur dict built-in, tetapi antarmuka publik untuk objek ini menggunakan notasi titik (pada dasarnya antarmuka hanya baca untuk beberapa data statis). Pengguna saya jauh lebih bahagia dengan 'foo.bar' daripada dengan 'foo ["bar"]' dan saya senang bahwa saya dapat mendukung fitur datatype dict.
Bryan Oakley

10
Anda sudah tahu gaya Python yang baik: kami memberi tahu Anda, jangan berpura-pura bahwa nilai-nilai dict adalah atribut. Ini praktik buruk. Misalnya, bagaimana jika Anda ingin menyimpan nilai dengan nama yang sama dengan atribut yang ada pada dict, seperti "item" atau "get" atau "pop"? Mungkin sesuatu yang membingungkan. Jadi jangan lakukan itu!
Larry Hastings

5
Ups, saya lupa tentang atribut seperti 'item', 'get' atau 'pop. Terima kasih telah menyampaikan contoh penting ini!
bodacydo

5
@ Gabe, sudah lama ... tapi saya pikir itu layak untuk dikatakan. Ini bukan "cukup baik di JS": itu "cukup mengerikan di JS". Menjadi lucu ketika Anda menyimpan kunci / attr yang memiliki nama yang sama dengan atribut penting lainnya dalam rantai prototip.
bgusach

11

Jika Anda ingin menggunakan kamus Anda yang dimodifikasi, Anda perlu menambahkan beberapa metode status ke jawaban di atas:

class DotDict(dict):
    """dot.notation access to dictionary attributes"""
    def __getattr__(self, attr):
        return self.get(attr)
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__

    def __getstate__(self):
        return self

    def __setstate__(self, state):
        self.update(state)
        self.__dict__ = self

Terima kasih atas komentarnya tentang acar. Saya tergila-gila dengan kesalahan ini dan hanya menyadari bahwa itu karena masalah ini!
Shagru

Juga terjadi ketika Anda menggunakan copy.deepcopy. Tambahan ini diperlukan.
user1363990

Penyederhanaan:__getattr__ = dict.get
martineau

9

Membangun jawaban Kugel dan mempertimbangkan kata-kata peringatan dari Mike Graham, bagaimana jika kita membuat bungkus?

class DictWrap(object):
  """ Wrap an existing dict, or create a new one, and access with either dot 
    notation or key lookup.

    The attribute _data is reserved and stores the underlying dictionary.
    When using the += operator with create=True, the empty nested dict is 
    replaced with the operand, effectively creating a default dictionary
    of mixed types.

    args:
      d({}): Existing dict to wrap, an empty dict is created by default
      create(True): Create an empty, nested dict instead of raising a KeyError

    example:
      >>>dw = DictWrap({'pp':3})
      >>>dw.a.b += 2
      >>>dw.a.b += 2
      >>>dw.a['c'] += 'Hello'
      >>>dw.a['c'] += ' World'
      >>>dw.a.d
      >>>print dw._data
      {'a': {'c': 'Hello World', 'b': 4, 'd': {}}, 'pp': 3}

  """

  def __init__(self, d=None, create=True):
    if d is None:
      d = {}
    supr = super(DictWrap, self)  
    supr.__setattr__('_data', d)
    supr.__setattr__('__create', create)

  def __getattr__(self, name):
    try:
      value = self._data[name]
    except KeyError:
      if not super(DictWrap, self).__getattribute__('__create'):
        raise
      value = {}
      self._data[name] = value

    if hasattr(value, 'items'):
      create = super(DictWrap, self).__getattribute__('__create')
      return DictWrap(value, create)
    return value

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

  def __getitem__(self, key):
    try:
      value = self._data[key]
    except KeyError:
      if not super(DictWrap, self).__getattribute__('__create'):
        raise
      value = {}
      self._data[key] = value

    if hasattr(value, 'items'):
      create = super(DictWrap, self).__getattribute__('__create')
      return DictWrap(value, create)
    return value

  def __setitem__(self, key, value):
    self._data[key] = value

  def __iadd__(self, other):
    if self._data:
      raise TypeError("A Nested dict will only be replaced if it's empty")
    else:
      return other


6

Saya suka Munch dan memberikan banyak opsi berguna di atas akses dot.

impor mengunyah

temp_1 = {'person': {'fname': 'senthil', 'lname': 'ramalingam'}}

dict_munch = munch.munchify (temp_1)

dict_munch.person.fname


6

Saya baru-baru ini menemukan ' Box perpustakaan ' ' yang melakukan hal yang sama.

Perintah instalasi: pip install python-box

Contoh:

from box import Box

mydict = {"key1":{"v1":0.375,
                    "v2":0.625},
          "key2":0.125,
          }
mydict = Box(mydict)

print(mydict.key1.v1)

Saya menemukan itu lebih efektif daripada perpustakaan lain yang ada seperti dotmap, yang menghasilkan kesalahan rekursi python ketika Anda memiliki dicts bersarang besar.

tautan ke perpustakaan dan detail: https://pypi.org/project/python-box/


5

Gunakan __getattr__, sangat sederhana, bekerja di Python 3.4.3

class myDict(dict):
    def __getattr__(self,val):
        return self[val]


blockBody=myDict()
blockBody['item1']=10000
blockBody['item2']="StackOverflow"
print(blockBody.item1)
print(blockBody.item2)

Keluaran:

10000
StackOverflow

4

Bahasa itu sendiri tidak mendukung ini, tetapi kadang-kadang ini masih merupakan persyaratan yang berguna. Selain resep Bunch, Anda juga dapat menulis metode kecil yang dapat mengakses kamus menggunakan string bertitik:

def get_var(input_dict, accessor_string):
    """Gets data from a dictionary using a dotted accessor-string"""
    current_data = input_dict
    for chunk in accessor_string.split('.'):
        current_data = current_data.get(chunk, {})
    return current_data

yang akan mendukung sesuatu seperti ini:

>> test_dict = {'thing': {'spam': 12, 'foo': {'cheeze': 'bar'}}}
>> output = get_var(test_dict, 'thing.spam.foo.cheeze')
>> print output
'bar'
>>

4

Untuk membangun berdasarkan jawaban epool, versi ini memungkinkan Anda untuk mengakses setiap dikt dalam melalui operator titik:

foo = {
    "bar" : {
        "baz" : [ {"boo" : "hoo"} , {"baba" : "loo"} ]
    }
}

Misalnya, foo.bar.baz[1].babakembali "loo".

class Map(dict):
    def __init__(self, *args, **kwargs):
        super(Map, self).__init__(*args, **kwargs)
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    if isinstance(v, dict):
                        v = Map(v)
                    if isinstance(v, list):
                        self.__convert(v)
                    self[k] = v

        if kwargs:
            for k, v in kwargs.iteritems():
                if isinstance(v, dict):
                    v = Map(v)
                elif isinstance(v, list):
                    self.__convert(v)
                self[k] = v

    def __convert(self, v):
        for elem in xrange(0, len(v)):
            if isinstance(v[elem], dict):
                v[elem] = Map(v[elem])
            elif isinstance(v[elem], list):
                self.__convert(v[elem])

    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(Map, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(Map, self).__delitem__(key)
        del self.__dict__[key]

1
Python 3: ganti iteritems()dengan items()dan xrange()denganrange()
sasawatc

3
def dict_to_object(dick):
    # http://stackoverflow.com/a/1305663/968442

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

    return Struct(**dick)

Jika seseorang memutuskan untuk secara permanen mengkonversi itu dictke objek ini harus dilakukan. Anda dapat membuat objek sekali pakai sebelum mengakses.

d = dict_to_object(d)

def attr (** kwargs): o = lambda: Tidak ada o .__ dict __. update (** kwargs) mengembalikan o
throws_exceptions_at_you

2

Saya akhirnya mencoba KEDUA AttrDict dan Bunchperpustakaan dan menemukan mereka menjadi cara 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 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, tetapi jika Anda menggunakan fungsi ini sedikit dalam kode 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.


0

Saya ingin melemparkan solusi saya sendiri ke atas ring:

https://github.com/skorokithakis/jsane

Ini memungkinkan Anda untuk mengurai JSON menjadi sesuatu yang dapat Anda akses with.attribute.lookups.like.this.r(), sebagian besar karena saya belum melihat jawaban ini sebelum mulai mengerjakannya.


Python bersalah atas beberapa kesalahan desain sederhana sial, membesarkan KeyErroradalah salah satunya, Ketika seseorang mengakses kunci yang tidak ada yang harus dilakukan hanyalah mengembalikan Nonemirip dengan perilaku JS. Saya penggemar autovivifikasi baik untuk membaca dan menulis. Perpustakaan Anda paling dekat dengan yang ideal.
nehem

0

Bukan jawaban langsung untuk pertanyaan OP, tetapi diilhami oleh dan mungkin bermanfaat bagi sebagian orang .. Saya telah membuat solusi berbasis objek menggunakan internal __dict__(Sama sekali tidak dioptimalkan kode)

payload = {
    "name": "John",
    "location": {
        "lat": 53.12312312,
        "long": 43.21345112
    },
    "numbers": [
        {
            "role": "home",
            "number": "070-12345678"
        },
        {
            "role": "office",
            "number": "070-12345679"
        }
    ]
}


class Map(object):
    """
    Dot style access to object members, access raw values
    with an underscore e.g.

    class Foo(Map):
        def foo(self):
            return self.get('foo') + 'bar'

    obj = Foo(**{'foo': 'foo'})

    obj.foo => 'foobar'
    obj._foo => 'foo'

    """

    def __init__(self, *args, **kwargs):
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    self.__dict__[k] = v
                    self.__dict__['_' + k] = v

        if kwargs:
            for k, v in kwargs.iteritems():
                self.__dict__[k] = v
                self.__dict__['_' + k] = v

    def __getattribute__(self, attr):
        if hasattr(self, 'get_' + attr):
            return object.__getattribute__(self, 'get_' + attr)()
        else:
            return object.__getattribute__(self, attr)

    def get(self, key):
        try:
            return self.__dict__.get('get_' + key)()
        except (AttributeError, TypeError):
            return self.__dict__.get(key)

    def __repr__(self):
        return u"<{name} object>".format(
            name=self.__class__.__name__
        )


class Number(Map):
    def get_role(self):
        return self.get('role')

    def get_number(self):
        return self.get('number')


class Location(Map):
    def get_latitude(self):
        return self.get('lat') + 1

    def get_longitude(self):
        return self.get('long') + 1


class Item(Map):
    def get_name(self):
        return self.get('name') + " Doe"

    def get_location(self):
        return Location(**self.get('location'))

    def get_numbers(self):
        return [Number(**n) for n in self.get('numbers')]


# Tests

obj = Item({'foo': 'bar'}, **payload)

assert type(obj) == Item
assert obj._name == "John"
assert obj.name == "John Doe"
assert type(obj.location) == Location
assert obj.location._lat == 53.12312312
assert obj.location._long == 43.21345112
assert obj.location.latitude == 54.12312312
assert obj.location.longitude == 44.21345112

for n in obj.numbers:
    assert type(n) == Number
    if n.role == 'home':
        assert n.number == "070-12345678"
    if n.role == 'office':
        assert n.number == "070-12345679"

0

Salah satu cara sederhana untuk mendapatkan akses dot (tetapi bukan akses array), adalah dengan menggunakan objek biasa dalam Python. Seperti ini:

class YourObject:
    def __init__(self, *args, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

... dan gunakan seperti ini:

>>> obj = YourObject(key="value")
>>> print(obj.key)
"value"

... untuk mengubahnya menjadi dict:

>>> print(obj.__dict__)
{"key": "value"}

0

Solusi ini merupakan penyempurnaan dari yang ditawarkan oleh epool untuk mengatasi persyaratan OP untuk mengakses dicts bersarang secara konsisten. Solusi oleh epool tidak memungkinkan untuk mengakses dicts bersarang.

class YAMLobj(dict):
    def __init__(self, args):
        super(YAMLobj, self).__init__(args)
        if isinstance(args, dict):
            for k, v in args.iteritems():
                if not isinstance(v, dict):
                    self[k] = v
                else:
                    self.__setattr__(k, YAMLobj(v))


    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(YAMLobj, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(YAMLobj, self).__delitem__(key)
        del self.__dict__[key]

Dengan kelas ini, satu sekarang dapat melakukan sesuatu seperti: A.B.C.D.


0

Ini juga berfungsi dengan dikte bersarang dan memastikan bahwa dikte yang ditambahkan nanti berperilaku sama:

class DotDict(dict):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Recursively turn nested dicts into DotDicts
        for key, value in self.items():
            if type(value) is dict:
                self[key] = DotDict(value)

    def __setitem__(self, key, item):
        if type(item) is dict:
            item = DotDict(item)
        super().__setitem__(key, item)

    __setattr__ = __setitem__
    __getattr__ = dict.__getitem__

0

Jawaban dari @ derek73 sangat rapi, tetapi tidak dapat diasamkan atau disalin, dan kembaliNone untuk kunci yang hilang. Kode di bawah ini memperbaiki ini.

Sunting: Saya tidak melihat jawaban di atas yang membahas titik yang sama persis (terbalik). Saya meninggalkan jawabannya di sini untuk referensi.

class dotdict(dict):
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

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

-1

Suatu jenis solusi yang halus

class DotDict(dict):

    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

    def __getattr__(self, key):

        def typer(candidate):
            if isinstance(candidate, dict):
                return DotDict(candidate)

            if isinstance(candidate, str):  # iterable but no need to iter
                return candidate

            try:  # other iterable are processed as list
                return [typer(item) for item in candidate]
            except TypeError:
                return candidate

            return candidate

        return typer(dict.get(self, key))
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.