2015-12-15 18 views
6

ho bisogno di una struttura di dati che memorizzano dizionario dizionari come si vede qui sotto:dizionario ordinato di dizionari ordinati in pitone

custom = {1: {'a': np.zeros(10), 'b': np.zeros(100)}, 
      2: {'c': np.zeros(20), 'd': np.zeros(200)}} 

Ma il problema è che io iterare su questa struttura dati molte volte nel mio codice. Ogni volta che lo iterato, ho bisogno che l'ordine di iterazione sia rispettato perché tutti gli elementi di questa complessa struttura di dati sono mappati su un array 1D (serializzato se volete), e quindi l'ordine è importante. Ho pensato di scrivere uno dict ordinato del dict ordinato per quella materia, ma non sono sicuro che questa sia la soluzione giusta in quanto sembra che stia scegliendo la struttura dei dati sbagliata. Quale sarebbe la soluzione più adeguata per il mio caso?

UPDATE

Quindi questo è quello che mi è venuto in mente finora:

class Test(list): 

    def __init__(self, *args, **kwargs): 

     super(Test, self).__init__(*args, **kwargs) 

     for k,v in args[0].items(): 
      self[k] = OrderedDict(v) 

     self.d = -1 
     self.iterator = iter(self[-1].keys()) 
     self.etype = next(self.iterator) 
     self.idx = 0 


    def __iter__(self): 
     return self 

    def __next__(self): 

     try: 
      self.idx += 1 
      return self[self.d][self.etype][self.idx-1] 

     except IndexError: 

      self.etype = next(self.iterator) 
      self.idx = 0 
      return self[self.d][self.etype][self.idx-1] 

    def __call__(self, d): 

     self.d = -1 - d 
     self.iterator = iter(self[self.d].keys()) 
     self.etype = next(self.iterator) 
     self.idx = 0 
     return self 


def main(argv=()): 

    tst = Test(elements) 
    for el in tst: 
     print(el) 
    # loop over a lower dimension 
    for el in tst(-2): 
     print(el) 

    print(tst) 


    return 0 

if __name__ == "__main__": 
    sys.exit(main()) 

posso scorrere le volte che voglio in questa struttura ordinata, e ho implementato __call__ così posso scorrere le dimensioni inferiori. Non mi piace il fatto che se non c'è una dimensione inferiore presente nell'elenco, non mi dà alcun errore. Ho anche la sensazione che ogni volta che chiamo return self[self.d][self.etype][self.idx-1] sia meno efficiente dell'iterazione originale sul dizionario. È vero? Come posso migliorare questo?

risposta

1

Potresti semplicemente usare un elenco di dizionari?

custom = [{'a': np.zeros(10), 'b': np.zeros(100)}, 
      {'c': np.zeros(20), 'd': np.zeros(200)}] 

Questo potrebbe funzionare se il dizionario esterno è l'unico necessario nell'ordine corretto. È comunque possibile accedere ai dizionari interni con custom[0] o custom[1] (attenzione, l'indicizzazione inizia ora allo 0).

Se vengono utilizzate non tutti gli indici, è possibile effettuare le seguenti operazioni:

custom = [None] * maxLength # maximum dict size you expect 

custom[1] = {'a': np.zeros(10), 'b': np.zeros(100)} 
custom[2] = {'c': np.zeros(20), 'd': np.zeros(200)} 
+0

Non posso usare questo perché 1 può o non può essere presente, e lo stesso vale per 0 e 2. – aaragon

+0

Ah ok, quindi in realtà hai bisogno delle chiavi in ​​"dict" esterno - non importa, scusa per l'equivoco! – Lisa

+0

@aaragon Ho modificato la risposta per conservare gli indici che erano le chiavi del tuo 'dict 'esterno e ho impostato tutti gli elementi non disponibili su' None'. – Lisa

0

si può risolvere l'ordine dei tasti mentre l'iterazione quando si ordinarli prima:

for key in sorted(custom.keys()): 
    print(key, custom[key]) 

Se Si desidera ridurre sorted() -call, si consiglia di memorizzare le chiavi in ​​un elenco aggiuntivo che verrà utilizzato come ordine di iterazione:

ordered_keys = sorted(custom.keys()) 
for key in ordered_keys: 
    print(key, custom[key]) 

Dovresti essere pronto a partire per quante più iterazioni sulla struttura dei dati, come ti serve.

+0

I itate molte volte su questa struttura dati. – aaragon

+0

La struttura viene ripetuta più volte nell'applicazione e ciò che voglio fare è fornire un approccio più user-friendly per digitare 'per k, v in custom.items(): per i, r in enumerate (v): # ecc. – aaragon

+0

Bene, questa sembra una domanda completamente diversa. – jbndlr

2

Penso che usare OrderedDict s sia il modo migliore. Sono incorporati e relativamente veloce:

custom = OrderedDict([(1, OrderedDict([('a', np.zeros(10)), 
             ('b', np.zeros(100))])), 
         (2, OrderedDict([('c', np.zeros(20)), 
             ('d', np.zeros(200))]))]) 

Se si vuole rendere più facile per scorrere i contenuti della vostra struttura dei dati, si può sempre fornire una funzione di utilità per farlo:

def iter_over_contents(data_structure): 
    for delem in data_structure.values(): 
     for v in delem.values(): 
      for row in v: 
       yield row 

Si noti che in Python 3.3+, che permette yield from <expression>, l'ultimo for loop può essere eliminato:

def iter_over_contents(data_structure): 
    for delem in data_structure.values(): 
     for v in delem.values(): 
      yield from v 

con una di quelle Sarai quindi in grado di scrivere qualcosa di simile:

for elem in iter_over_contents(custom): 
    print(elem) 

e nascondere la complessità.

Mentre si potrebbe definire la propria classe, nel tentativo di incapsulare questa struttura dati e usare qualcosa come la funzione di generatore di iter_over_contents() come metodo __iter__(), tale approccio sarebbe probabilmente più lento e non permetterebbe espressioni utilizzando due livelli di indicizzazione come questo seguenti:

custom[1]['b'] 

che usando dizionari nidificate (o OrderedDefaultdict s come mostrato nella mia altra risposta) sarebbe.

2

Ecco un'altra alternativa che utilizza uno OrderedDefaultdict per definire la struttura di dati ad albero che si desidera. Sto riutilizzando la definizione di questo da un altro answer mio.

Per utilizzarlo, è necessario assicurarsi che le voci siano definite nell'ordine in cui si desidera accedervi in ​​seguito.

class OrderedDefaultdict(OrderedDict): 
    def __init__(self, *args, **kwargs): 
     if not args: 
      self.default_factory = None 
     else: 
      if not (args[0] is None or callable(args[0])): 
       raise TypeError('first argument must be callable or None') 
      self.default_factory = args[0] 
      args = args[1:] 
     super(OrderedDefaultdict, self).__init__(*args, **kwargs) 

    def __missing__ (self, key): 
     if self.default_factory is None: 
      raise KeyError(key) 
     self[key] = default = self.default_factory() 
     return default 

    def __reduce__(self): # optional, for pickle support 
     args = (self.default_factory,) if self.default_factory else() 
     return self.__class__, args, None, None, self.iteritems() 

Tree = lambda: OrderedDefaultdict(Tree) 

custom = Tree() 
custom[1]['a'] = np.zeros(10) 
custom[1]['b'] = np.zeros(100) 
custom[2]['c'] = np.zeros(20) 
custom[2]['d'] = np.zeros(200) 

Non sono sicuro di aver capito la tua domanda successiva. Se la struttura dei dati è limitata a due livelli, è possibile utilizzare i cicli nidificati for per iterare sugli elementi nell'ordine in cui sono stati definiti. Per esempio:

for key1, subtree in custom.items(): 
    for key2, elem in subtree.items(): 
     print('custom[{!r}][{!r}]: {}'.format(key1, key2, elem)) 

(In Python 2 che ci si vuole utilizzare iteritems() invece di items().)

+0

Grazie per la soluzione. Se dovessi fornire un'interfaccia utente più amichevole per iterare attraverso gli elementi nel tuo dizionario in ordine, è possibile? Dì se voglio fare 'per el in custom:' e poi andando individualmente attraverso ogni elemento in maniera ordinata, come faresti? – aaragon

+0

Quello che intendevo è che sarebbe bello attraversare l'intera struttura dati in ordine usando un solo ciclo. Sto cercando di farlo sovrascrivendo i metodi '__iter__' e' __next__', ma sto fallendo miseramente. Volevo anche chiederti se potresti spiegare un po 'il codice che hai scritto perché è Python piuttosto avanzato per me. – aaragon

+0

Per i dizionari, i metodi '__iter __()' e 'next()' (non '__next__') passano sopra le chiavi del dizionario, tuttavia una struttura ad albero ricorsiva basata sui dizionari potrebbe supportare più di una forma di iterazione - sia la larghezza - per esempio, prima e profondità prima di attraversare. Inoltre, i dizionari hanno un metodo 'items()' che restituisce copie di tutte le coppie '(chiave, valore)' che contiene. Con queste differenze non è chiaro cosa stai cercando di realizzare quando dici solo che vuoi ripetere gli elementi in ordine. Quale ordine e cosa consideri un "elemento" in questo contesto? – martineau

Problemi correlati