2013-04-29 10 views
26

Sono molto nuovo per Python e vorrei poter fare notazione . per accedere all'oggetto dict in python.Come usare la notazione a punti per dict in python?

Diciamo che ho test come questo:

>>>test = dict() 
>>> test['name'] = 'value' 
>>> print(test['name']) 
value 

Ma vorrei poter fare test.name per ottenere value. Infatti ho fatto l'override del metodo di __getattr__ nella mia classe in questo modo:

class JuspayObject: 

def __init__(self,response): 
    self.__dict__['_response'] = response 

def __getattr__(self,key): 
    try: 
     val = self._response[key] 
     return val 
    except KeyError,err: 
     sys.stderr.write('Sorry no key matches') 

e questo funziona! quando lo faccio:

test.name //I get value. 

ma il problema è quando ho appena stampo test solo ottengo l'errore come:

'Sorry no key matches' 

perché questo accada !!!

Grazie in anticipo.

+0

È necessario chiamare super-classe __getattr__ quando si richiede un attributo che non è nel dettato. –

+0

@DavidHeffernan Una classe vecchio stile come l'esempio dell'OP ha anche una superclasse? – Aya

+0

@Aya Nessuna idea. In caso contrario, utilizzare una nuova classe di stile. Chi usa ancora le classi di vecchio stile comunque? –

risposta

23

presumo che si sono confortevoli in Javascript e vuole prendere in prestito quel tipo di sintassi ... Posso dirti per esperienza personale che questa non è una grande idea.

Di sicuro sembra meno dettagliato e ordinato; ma alla lunga è solo oscuro. I dicts sono dicts e cercare di farli comportarsi come oggetti con attributi probabilmente porterà a (cattive) sorprese.

Se è necessario manipolare i campi di un oggetto come se fossero un dizionario, si può sempre ricorrere a utilizzare l'attributo interno __dict__ quando ne avete bisogno, e poi è esplicitamente cosa chiara che si stanno facendo. Oppure utilizzare getattr(obj, 'key') per tenere conto anche della struttura di ereditarietà e degli attributi di classe.

Ma leggendo il tuo esempio sembra che tu stia provando qualcosa di diverso ... Come l'operatore di punti guarderà già nell'attributo __dict__ senza alcun codice aggiuntivo.

+3

Esempio di brutta sorpresa: se hai una chiave nel dict che sembra essere una parola chiave python, ad es. la stringa ''for'', quindi l'accesso agli attributi fallisce e non c'è un modo elegante per gestire correttamente questo caso. L'intera idea è fondamentalmente spezzata sin dall'inizio. – wim

+0

D'altra parte, il completamento automatico rende più efficiente, meno codice soggetto a errori. Funzionerebbe perfettamente * Se la struttura del dict può essere predefinita. * – roundar

2

Hai bisogno di un named tuple?

from collections import namedtuple 
Test = namedtuple('Test', 'name foo bar') 
my_test = Test('value', 'foo_val', 'bar_val') 
print(my_test) 
print(my_test.name) 

+0

Questo è un modo interessante per accedere a una struttura dati tramite notazione puntata, ma non sembra particolarmente compatibile con JSON o dict. Ci sono librerie che usano tuple con nome sotto le copertine che forniscono supporto JSON e dict. Prova https://github.com/dsc/bunch o https://github.com/kennknowles/python-jsonpath-rw – MarkHu

28

Questa funzionalità già exists in the standard libraries, quindi vi consiglio di usare solo la loro classe.

>>> from types import SimpleNamespace 
>>> d = {'key1': 'value1', 'key2': 'value2'} 
>>> n = SimpleNamespace(**d) 
>>> print(n) 
namespace(key1='value1', key2='value2') 
>>> n.key2 
'value2' 

Aggiunta, modifica e rimozione dei valori si ottiene con il regolare accesso attributo, cioè è possibile utilizzare istruzioni come n.key = val e del n.key.

Per tornare ad un dict di nuovo:

>>> vars(n) 
{'key1': 'value1', 'key2': 'value2'} 

Ovviamente, le chiavi in ​​dict dovrebbe essere identificatori di stringa per l'accesso attributo per funzionare correttamente.

In Python 3.3 è stato aggiunto uno spazio dei nomi semplice. Per le versioni precedenti della lingua, argparse.Namespace ha un comportamento simile.

+0

[Fabric] (http://www.fabfile.org) ha anche un buon [implementazione minima] ] (https://github.com/fabric/fabric/blob/1.13.1/fabric/utils.py#L186), che ho anche postato [qui] (http://stackoverflow.com/a/41274937/307614). – Dave

2

È necessario fare attenzione quando si utilizza __getattr__, perché è utilizzato per molte funzionalità di Python integrate.

provare qualcosa di simile ...

class JuspayObject: 

    def __init__(self,response): 
     self.__dict__['_response'] = response 

    def __getattr__(self, key): 
     # First, try to return from _response 
     try: 
      return self.__dict__['_response'][key] 
     except KeyError: 
      pass 
     # If that fails, return default behavior so we don't break Python 
     try: 
      return self.__dict__[key] 
     except KeyError: 
      raise AttributeError, key 

>>> j = JuspayObject({'foo': 'bar'}) 
>>> j.foo 
'bar' 
>>> j 
<__main__.JuspayObject instance at 0x7fbdd55965f0> 
+1

'__getattr__' è usato come fallback solo se nessuna altra regola di ricerca corrisponde, quindi non c'è bisogno di cercare' self .__ dict __ [chiave] ', è già stato fatto se viene chiamato' __getattr__'. Basta generare 'AttributeError' se la ricerca su self._response ['chiave'] fallita è sufficiente. –

+0

@brunodesthuilliers Sembra che tu abbia ragione. Strano. Forse questo era solo necessario tornare ai tempi di Python v1.5. – Aya

2

__getattr__ è usato come ripiego quando tutte le altre regole attributo di ricerca hanno fallito. Quando provi a "stampare" il tuo oggetto, Python cerca un metodo __repr__, e dal momento che non lo implementa nella tua classe finisce con il chiamare __getattr__ (sì, anche in Python i metodi sono attributi). Non si deve assumere quale chiave getattr verrà chiamata con, e, cosa più importante, __getattr__ deve generare un AttributeError se non può risolvere key.

Come nota a margine: non utilizzare self.__dict__ per l'accesso all'attributo ordinaria, basta usare l'attributo pianura notazione:

class JuspayObject: 

    def __init__(self,response): 
     # don't use self.__dict__ here 
     self._response = response 

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

Ora, se la classe non ha altra responsabilità (e la vostra versione di Python è> = 2.6 e non è necessario supportare versioni precedenti), è possibile utilizzare solo un namedtuple: http://docs.python.org/2/library/collections.html#collections.namedtuple

Problemi correlati