Come usare un punto "." accedere ai membri del dizionario?


283

Come posso rendere accessibili i membri del dizionario Python tramite un punto "."?

Ad esempio, invece di scrivere mydict['val'], mi piacerebbe scrivere mydict.val.

Inoltre, vorrei accedere ai dadi nidificati in questo modo. Per esempio

mydict.mydict2.val 

farebbe riferimento a

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

20
Molte delle situazioni in cui le persone usano i dadi annidati sarebbero ugualmente o meglio servite dai dadi con le tuple come chiavi, dove d[a][b][c]viene sostituito da d[a, b, c].
Mike Graham,

7
Non è magico: foo = {}; foo [1,2,3] = "uno, due, tre!"; foo.keys () => [(1,2,3)]
Bryan Oakley,

10
Wow. Wow di nuovo. Non sapevo che le tuple potessero essere le chiavi del dict. Wow per la terza volta.
bodacydo,

3
Qualsiasi oggetto "hash" può essere usato come chiave di un dict. La maggior parte degli oggetti immutabili è anche hash, ma solo se tutti i loro contenuti sono hash. Il codice d [1, 2, 3] funziona perché "," è "crea un operatore tupla"; è uguale a d [(1, 2, 3)]. Le parentesi sono spesso facoltative attorno alla dichiarazione di una tupla.
Larry Hastings

6
Hai considerato il caso in cui la chiave ha un punto da sola - {"my.key":"value"}? O quando la chiave è una parola chiave, come "da"? L'ho considerato un paio di volte, ed è più problemi e risoluzione dei problemi che benefici percepiti.
Todor Minakov,

Risposte:


147

Puoi farlo usando questa classe che ho appena creato. Con questa classe puoi usare l' Mapoggetto come un altro dizionario (inclusa la serializzazione json) o con la notazione punto. Spero di aiutarti:

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]

Esempi di utilizzo:

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
Per lavorare su Python 3 ho aggiornato .iteritems()a.items()
berto

13
Si noti che questo si comporterà diversamente dalle aspettative comuni in quanto non aumenterà AttributeErrorse l'attributo non esiste. Invece tornerà None.
mic_e

Consiglia di aggiungere getstate e setstate in modo che la copia profonda e altri sistemi possano supportarlo.
user1363990,

4
Puoi semplificare il tuo costruttore self.update(*args,**kwargs). Inoltre, puoi aggiungere __missing__(self,key): value=self[key]= type(self)(); return value. Quindi è possibile aggiungere voci mancanti usando la notazione punto. Se vuoi che sia selezionabile, puoi aggiungere __getstate__e__setstate__
Jens Munk il

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

266

L'ho sempre tenuto presente in un file util. Puoi usarlo come mixin anche nelle tue lezioni.

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
Risposta molto semplice, eccezionale! Sai per caso cosa dovrei fare per far funzionare il completamento delle schede in IPython? La classe dovrebbe implementare __dir __ (self), ma in qualche modo non riesco a farlo funzionare.
andreas-h

8
+1 per semplicità. ma non sembra funzionare su dadi nidificati. d = {'foo': {'bar': 'baz'}}; d = dotdict(d); d.foo.bargenera un errore di attributo, ma d.foofunziona bene.
tmthyjames,

2
Sì, questo non funziona per strutture nidificate complesse.
David,

17
@tmthyjames potresti semplicemente restituire un oggetto di tipo dotdict nel metodo getter per accedere in modo ricorsivo agli attributi con notazione punti come: 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
Dopo aver sperimentato, sembra getdavvero una cattiva idea poiché tornerà Noneinvece di generare un errore per gli articoli mancanti ...
NichtJens

117

Installa dotmaptramitepip

pip install dotmap

Fa tutto quello che vuoi che faccia e subclasse dict, quindi funziona come un normale dizionario:

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'

Inoltre, puoi convertirlo in e da dictoggetti:

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

Ciò significa che se qualcosa a cui si desidera accedere è già in dictforma, è possibile trasformarlo in un DotMapper un facile accesso:

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

Infine, crea automaticamente nuove DotMapistanze figlio in modo da poter fare cose del genere:

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

Confronto con il mazzo

Divulgazione completa: sono il creatore di DotMap . L'ho creato perché Bunchmancavano queste funzionalità

  • ricordando che gli articoli dell'ordine vengono aggiunti e ripetono in quell'ordine
  • DotMapcreazione automatica dei figli , che consente di risparmiare tempo e rendere il codice più pulito quando si dispone di molta gerarchia
  • costruendo da una dicte ricorsivamente convertendo tutte le dictistanze figlio inDotMap

2
:-) riesci a farlo funzionare con i tasti che hanno già un punto nel nome? {"test.foo": "bar"}è possibile accedere tramite mymap.test.fooSarebbe fantastico. Ci vorrà un po 'di regressione per convertire una mappa piatta in una mappa profonda, quindi applicare DotMap ad essa, ma ne vale la pena!
dlite922,

Neat. Un modo per far funzionare l'elenco / completamento delle schede con le chiavi del taccuino Jupyter? L'accesso in stile punto è molto utile per l'uso interattivo.
Dmitri,

@Dmitri Cool prodotto. Non l'ho mai sentito prima, quindi non sono sicuro di come far funzionare il suo completamento automatico. Sono d'accordo che l'uso DotMapcon il completamento automatico funziona meglio. Uso Sublime Text, che completa automaticamente le parole chiave precedentemente digitate.
Chris Redford,

1
Trovo che manchi l'estrazione del dizionario per cose come **kwargso c = {**a, **b}. In effetti, fallisce silenziosamente, si comporta come un dizionario vuoto durante l'estrazione.
Simon Streicher,

@SimonStreicher Ho provato questo m = DotMap(); m.a = 2; m.b = 3; print('{a} {b}'.format(**m));e ho ottenuto l'atteso 2 3. Se hai un caso risolto che funziona dict()ma che non funziona DotMap(), ti preghiamo di inviare il codice alla scheda Problemi in GitHub.
Chris Redford,

56

Deriva da dict e attua __getattr__e __setattr__.

Oppure puoi usare Bunch che è molto simile.

Non credo sia possibile eseguire il monkeypatch della classe dict integrata.


2
Cosa significa esattamente il monkeypatch? Ne ho sentito parlare ma non usato. (Mi dispiace di aver fatto domande così da principiante, non sono ancora così bravo con la programmazione (sono solo uno studente del 2 ° anno.))
bodacydo,

9
Il monkeypatch sta usando la dinamicità di Python (o qualunque linguaggio) per cambiare qualcosa che verrebbe normalmente definito nel codice sorgente. Si applica in particolare alla modifica della definizione delle classi dopo la loro creazione.
Mike Graham,

Se usi molto questa funzionalità, fai attenzione alla velocità di Bunch. Lo stavo usando abbastanza frequentemente e alla fine ho consumato un terzo del mio tempo di richiesta. Controlla la mia risposta per una spiegazione più dettagliata di questo.
JayD3e

22

Fabric ha un'implementazione davvero piacevole e minima . Estendendolo per consentire l'accesso nidificato, possiamo usare a defaultdict, e il risultato è simile al seguente:

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

Usalo come segue:

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

Ciò elabora un po 'la risposta di Kugel "Deriva dal dict, e implementa __getattr__e __setattr__". Ora sai come!


1
Quello è fantastico!
Thomas Klinger,

Bello includere un dict predefinito, tuttavia questo sembra funzionare solo quando si avvia un dict da zero. Se abbiamo bisogno di convertire un dict esistente in "dotdict" ricorsivamente. Ecco un'alternativa dotdictche consente di convertire dictricorsivamente oggetti esistenti : gist.github.com/miku/…
miku

19

Ho provato questo:

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

puoi provare __getattribute__anche tu .

rendere ogni dict un tipo di dotdict sarebbe abbastanza buono, se vuoi iniziare questo da un dict multistrato, prova __init__anche a implementare .


oops, la risposta di @Kugel è simile.
martedì

1
tdihp, mi piace ancora la tua risposta perché l'ho capita più velocemente - ha il codice attuale.
Yigal,

1
+1 per il codice effettivo. Ma il suggerimento di @ Kugel di usare Bunch è anche molto buono.
Dannid,

Ho trovato utile incorporare questa funzione all'interno di una funzione posizionandola def docdict(name):prima di essa e quindi "if isinstance (name, dict): return DotDict (name) return name"
Daniel Moskovich

ottimo esempio semplice .. Ho esteso un po 'questo in modo che un dict nidificato sia facilmente concatenato, simile a @DanielMoskovich, ma restituisce anche i nodi foglia correttamente per int, stringa, ecc ... o null se non trovatoclass 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

Non farlo. L'accesso agli attributi e l'indicizzazione sono cose separate in Python e non dovresti desiderare che eseguano lo stesso. Crea una classe (possibilmente una creata da namedtuple) se hai qualcosa che dovrebbe avere attributi accessibili e usa la []notazione per ottenere un oggetto da un dict.


Grazie per la risposta. Ma dai un'occhiata a questa domanda che ho anche posto: stackoverflow.com/questions/2352252/… Questa sembra una buona idea da usare .invece di []accedere a complicate strutture di dati nei template di Mako.
bodacydo,

2
Vedo un caso d'uso per questo; in effetti l'ho fatto solo un paio di settimane fa. Nel mio caso volevo un oggetto a cui potessi accedere agli attributi con notazione punto. Ho trovato molto semplice ereditare semplicemente da dict, quindi ho incorporato tutte le funzionalità di dict, ma l'interfaccia pubblica per questo oggetto utilizza la notazione a punti (è essenzialmente un'interfaccia di sola lettura per alcuni dati statici). I miei utenti sono molto più felici con 'foo.bar' che con 'foo ["bar"]' e sono contento di poter recuperare le funzionalità del tipo di dati dict.
Bryan Oakley,

10
Conosci già bene lo stile Python: ti stiamo dicendo, non far finta che i valori di un dict siano attributi. È una cattiva pratica. Ad esempio, cosa succede se si desidera memorizzare un valore con lo stesso nome di un attributo esistente di un dict, come "articoli" o "get" o "pop"? Probabilmente qualcosa di confuso. Quindi non farlo!
Larry Hastings

5
Oops, ho dimenticato attributi come 'items', 'get' o 'pop. Grazie per aver sollevato questo importante esempio!
bodacydo

5
@Gabe, è passato molto tempo ... ma penso che valga la pena dirlo. Non è "abbastanza buono in JS": è "abbastanza orribile in JS". Diventa divertente quando memorizzi chiavi / attr che hanno lo stesso nome di altri attributi importanti nella catena prototipica.
bgusach,

11

Se desideri decapare il dizionario modificato, devi aggiungere alcuni metodi di stato alle risposte sopra:

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

Grazie per il commento sul decapaggio. Sono stato impazzito da questo errore e ho capito solo che era a causa di questo problema!
Shagru,

Succede anche quando si utilizza copy.deepcopy. Questa aggiunta è necessaria.
user1363990,

Semplificazione:__getattr__ = dict.get
martineau

9

Basandomi sulla risposta di Kugel e prendendo in considerazione le parole di cautela di Mike Graham, cosa succede se creiamo un involucro?

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

Mi piace il Munch e offre molte opzioni utili oltre all'accesso ai punti.

import munch

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

dict_munch = munch.munchify (temp_1)

dict_munch.person.fname


6

Di recente mi sono imbattuto nel ' Box libreria " che fa la stessa cosa.

Comando di installazione: pip install python-box

Esempio:

from box import Box

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

print(mydict.key1.v1)

Ho scoperto che è più efficace di altre librerie esistenti come dotmap, che generano errori di ricorsione in Python quando si hanno dadi nidificati di grandi dimensioni.

collegamento alla biblioteca e dettagli: https://pypi.org/project/python-box/


5

Utilizzare __getattr__, molto semplice, funziona in 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)

Produzione:

10000
StackOverflow

4

Il linguaggio stesso non lo supporta, ma a volte questo è ancora un requisito utile. Oltre alla ricetta del mazzo, puoi anche scrivere un piccolo metodo che può accedere a un dizionario usando una stringa punteggiata:

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

che sosterrebbe qualcosa del genere:

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

4

Per basarsi sulla risposta di epool, questa versione consente di accedere a qualsiasi dict all'interno tramite l'operatore punto:

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

Ad esempio, foo.bar.baz[1].babarestituisce "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: sostituisci iteritems()con items()e xrange()conrange()
sasawatc il

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)

Se uno decide di convertirlo definitivamente dictin oggetto, ciò dovrebbe fare. È possibile creare un oggetto usa e getta appena prima dell'accesso.

d = dict_to_object(d)

def attr (** kwargs): o = lambda: None o .__ dict __. update (** kwargs) return o
throws_exceptions_at_you

2

Ho finito per provare ENTRAMBI l' attrice e il mazzolibrerie e li ho trovati come un modo per rallentare per i miei usi. Dopo che un amico e io lo abbiamo esaminato, abbiamo scoperto che il metodo principale per scrivere queste librerie risulta che la libreria ricorre in modo aggressivo attraverso un oggetto nidificato e fa copie dell'oggetto dizionario in tutto. Con questo in mente, abbiamo apportato due modifiche chiave. 1) Abbiamo creato gli attributi a caricamento lento 2) invece di creare copie di un oggetto dizionario, creiamo copie di un oggetto proxy leggero. Questa è l'implementazione finale. L'aumento delle prestazioni dell'utilizzo di questo codice è incredibile. Quando si utilizza AttrDict o Bunch, queste due librerie da sole hanno consumato rispettivamente 1/2 e 1/3 del tempo della mia richiesta (cosa !?). Questo codice ha ridotto quel tempo a quasi nulla (da qualche parte nell'intervallo di 0,5 ms). Questo ovviamente dipende dalle tue esigenze, ma se stai usando questa funzionalità un po 'nel tuo codice,

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

Vedi l'implementazione originale qui da https://stackoverflow.com/users/704327/michael-merickel .

L'altra cosa da notare è che questa implementazione è piuttosto semplice e non implementa tutti i metodi di cui potresti aver bisogno. Dovrai scriverli come richiesto sugli oggetti DictProxy o ListProxy.


0

Vorrei buttare la mia soluzione sul ring:

https://github.com/skorokithakis/jsane

Ti consente di analizzare JSON in qualcosa a cui puoi accedere with.attribute.lookups.like.this.r(), soprattutto perché non avevo visto questa risposta prima di iniziare a lavorarci.


Python è colpevole di alcuni fastidiosi errori di progettazione semplice, il rilancio KeyErrorè uno di questi, quando si accede alla chiave che non esiste tutto ciò che deve fare è tornare Nonesimile al comportamento di JS. Sono un grande fan dell'autovivificazione sia per la lettura che per la scrittura. La tua biblioteca è la più vicina all'ideale.
nehem,

0

Non una risposta diretta alla domanda del PO, ma ispirata e forse utile per alcuni .. Ho creato una soluzione basata su oggetti usando il __dict__codice interno (in nessun modo ottimizzato)

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

Un modo semplice per ottenere l'accesso ai punti (ma non l'accesso all'array) è usare un oggetto semplice in Python. Come questo:

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

... e usalo così:

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

... per convertirlo in un dict:

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

0

Questa soluzione è un perfezionamento di quella offerta da epool per rispondere al requisito dell'OP di accedere a dadi annidati in modo coerente. La soluzione di epool non ha consentito l'accesso a dadi nidificati.

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]

Con questa classe, si può ora fare qualcosa di simile: A.B.C.D.


0

Questo funziona anche con i dadi nidificati e si assicura che i dadi aggiunti in seguito si comportino allo stesso modo:

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

La risposta di @ derek73 è molto chiara , ma non può essere decapata né copiata (in profondità) e restituisce Nonele chiavi mancanti. Il codice seguente risolve questo problema.

Modifica: non ho visto la risposta sopra che riguarda esattamente lo stesso punto (votato). Lascio qui la risposta come riferimento.

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

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

-1

Una soluzione delicata

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))
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.