2009-03-16 22 views
66

Un sacco di volte in Perl, farò qualcosa di simile:Qual è il modo migliore per inizializzare una dict di dict in Python?

$myhash{foo}{bar}{baz} = 1 

Come dovrei tradurre questo a Python? Finora ho:

if not 'foo' in myhash: 
    myhash['foo'] = {} 
if not 'bar' in myhash['foo']: 
    myhash['foo']['bar'] = {} 
myhash['foo']['bar']['baz'] = 1 

C'è un modo migliore?

+0

ahem, infatti l'altro è stato chiesto 5 giorni prima e questi entrambi hanno 4 anni ... –

risposta

83
class AutoVivification(dict): 
    """Implementation of perl's autovivification feature.""" 
    def __getitem__(self, item): 
     try: 
      return dict.__getitem__(self, item) 
     except KeyError: 
      value = self[item] = type(self)() 
      return value 

Testing:

a = AutoVivification() 

a[1][2][3] = 4 
a[1][3][3] = 5 
a[1][2]['test'] = 6 

print a 

uscita:

{1: {2: {'test': 6, 3: 4}, 3: {3: 5}}} 
+3

È possibile estenderlo in modo che supporti il ​​seguente comportamento: a [1] [2] [3] + = some_value. Quindi, se la chiave non esistesse in anticipo, allora a [1] [2] [3] verrebbe inizializzata con il valore predefinito del tipo (some_value)? –

+4

Questa funzione ha l'effetto collaterale che ogni tentativo di ottenere una chiave inesistente crea anche la chiave. In genere, si desidera creare automaticamente una chiave solo se si stava contemporaneamente impostando una chiave o una sottochiave. –

+0

C'è anche un modo per rendere la variabile di assegnazione? Quindi dato 'var = [1,2,3]', potrei fare come 'a [var] = 1', che si espanderebbe in' a [1] [2] [3] = 1'? – PascalVKooten

2

Credo che la traduzione letterale sarebbe:

mydict = {'foo' : { 'bar' : { 'baz':1}}} 

Calling:

>>> mydict['foo']['bar']['baz'] 

ti dà 1.

Che sembra un po 'sgradevoli per me, però.

(io non sono un ragazzo perl, però, così sto cercando di indovinare a che cosa il vostro perl fa)

+1

Questo funziona solo al momento dell'inizializzazione, però, giusto? – mike

+0

Non sono sicuro di cosa intendi. – Dana

+0

@Dana, invece di aggiungere nuovi valori a mydict durante il runtime. –

2

dizionari nidificati del genere sono (spesso) chiamato un mans poveri oggetti. Sì, c'è un'implicazione e potrebbe essere correlata con la natura orientata agli oggetti dei pitoni.

12

c'è un motivo ha bisogno di essere un dict di dicts? Se non c'è alcun motivo valido per quella particolare struttura, si potrebbe semplicemente indicizzare il dict con una tupla:

mydict = {('foo', 'bar', 'baz'):1} # Initializes dict with a key/value pair 
mydict[('foo', 'bar', 'baz')]  # Returns 1 

mydict[('foo', 'unbar')] = 2  # Sets a value for a new key 

Le parentesi sono necessarie se si inizializza il dict con una chiave tuple, ma è possibile omettere quando l'impostazione/ricevendo valori utilizzando []:

mydict = {}      # Initialized the dict 
mydict['foo', 'bar', 'baz'] = 1 # Sets a value 
mydict['foo', 'bar', 'baz']  # Returns 1 
+0

Puoi essere chiaro quando puoi omettere le parentesi? È perché la virgola è l'operatore tupla e le parentesi sono necessarie solo se abbiamo un raggruppamento ambiguo? – Kiv

+0

Chiarimento aggiunto, grazie. – zweiterlinde

+0

Questo potrebbe essere effettivamente più veloce dei dizionari nidificati perché esiste una ricerca anziché tre. – ChaimG

86

Se è fissato l'importo di nidificazione è necessario, collections.defaultdict è meraviglioso.

ad es. nidificazione due profonde:

myhash = collections.defaultdict(dict) 
myhash[1][2] = 3 
myhash[1][3] = 13 
myhash[2][4] = 9 

Se si vuole andare un altro livello di nidificazione, avrete bisogno di fare qualcosa di simile:

myhash = collections.defaultdict(lambda : collections.defaultdict(dict)) 
myhash[1][2][3] = 4 
myhash[1][3][3] = 5 
myhash[1][2]['test'] = 6 

edit: MizardX sottolinea che siamo in grado di ottenere la piena genericità con un semplice funzione:

import collections 
def makehash(): 
    return collections.defaultdict(makehash) 

Ora possiamo fare:

myhash = makehash() 
myhash[1][2] = 4 
myhash[1][3] = 8 
myhash[2][5][8] = 17 
# etc 
+4

o def makehash(): restituisce collections.defaultdict (makehash); myhash = makehash() –

+0

Non ho problemi con le funzioni ricorsive "tradizionali", ma c'è qualcosa che non trovo intuitivo. Dispari. Comunque grazie! –

+1

Grazie per questo. That lambda: defaultdict() è ciò di cui avevo bisogno. – wheaties