2016-03-01 26 views
5

Ho scritto un codice per l'ottimizzazione del progetto utilizzando la libreria Inspyred e la sua implementazione di Algoritmi genetici. In sostanza, il processo di ottimizzazione crea un gran numero di variazioni su una singola struttura di dati, che è un dizionario annidato nel mio caso.Dizionario Python che memorizza solo le modifiche

Per ridurre la quantità di memoria utilizzata nel processo, ho cercato di creare una sorta di tipo di dizionario differenziale, che memorizza solo elementi diversi dal dizionario di base. La ragione di ciò è che in un caso tipico, il 95% dei dati nella struttura dati non verrà modificato in nessuna delle varianti, ma qualsiasi parte della struttura dati può contenere variazioni. Quindi, per motivi di flessibilità, mi piacerebbe avere un tipo di dati che si comporta più o meno come un dizionario, ma memorizza solo le modifiche.

questo è il risultato del mio tentativo di creare questa:

#!/usr/bin/python 

import unittest 
import copy 

global_base={} 

class DifferentialDict(object): 
    """ 
    dictionary with differential storage of changes 
    all DifferentialDict objects have the same base dictionary 
    """ 

    def __init__(self,base=None): 
     global global_base 

     self.changes={} 

     if not base==None: 
      self.set_base(base) 

    def set_base(self,base): 
     global global_base 
     global_base=copy.deepcopy(base) 

    def __copy__(self): 
     return self 

    def __deepcopy__(self): 
     new=DifferentialDict() 
     new.changes=copy.deepcopy(self.changes) 
     return new 

    def get(self): 
     global global_base 
     outdict=copy.deepcopy(global_base) 
     for key in self.changes: 
      outdict[key]=self.changes[key] 
     return outdict 

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

    def __getitem__(self,key): 
     global global_base 
     if key in self.changes: 
      return self.changes[key] 
     else: 
      return global_base[key] 

class TestDifferentialDict(unittest.TestCase): 
    def test1(self): 
     ldict={'a':{1:2,3:4},'b':{'c':[1,2,3],'d':'abc'}} 
     ddict=DifferentialDict(base=ldict) 

     self.assertEqual(ddict['a'],{1:2,3:4}) 
     ddict['a']=5 
     self.assertEqual(ddict['a'],5) 

    def test2(self): 
     ldict={'a':{1:2,3:4},'b':{'c':[1,2,3],'d':'abc'}} 
     ddict1=DifferentialDict(base=ldict) 
     ddict2=DifferentialDict(base=ldict) 

     ddict1['a'][3]=5 
     ddict2['a'][3]=7 
     self.assertEqual(ddict1['a'][3],5) 
     self.assertEqual(ddict2['a'][3],7) 

    def test3(self): 
     ldict={'a':{1:2,3:4},'b':{'c':[1,2,3],'d':'abc'}} 
     ddict1=DifferentialDict(base=ldict) 
     ddict2=ddict1.__deepcopy__() 

     ddict1['a'][3]=5 
     ddict2['a'][3]=7 
     self.assertEqual(ddict1['a'][3],5) 
     self.assertEqual(ddict2['a'][3],7) 



if __name__ == "__main__": 
    unittest.main() 

Funziona bene per un dizionario semplice, ma si rompe quando i nuovi dizionari sono annidati all'interno del dizionario principale. Capisco che ciò avvenga perché questi dizionari di secondo livello sono veri dizionari Python invece di istanze del mio DifferentialDict, risultanti in una sovrascrittura di voci in global_base piuttosto che in modifiche in self.changes. Tuttavia, devono essere a causa della premessa che tutte le istanze di DifferentialDict condividono lo stesso dizionario di base. Potrei aggiungere una chiave "entry level" a ogni istanza di DifferentialDict, ma la mia sensazione è che ci sia una soluzione più elegante che mi sfugge.

Apprezzerei molto qualsiasi suggerimento su come far funzionare il mio dizionario differenziale quando nidificato. Grazie in anticipo!

+0

Quali test stanno passando e quali falliscono? – RafaelC

+0

I test 2 e 3 falliscono, 1 passaggi. –

+0

Ho appena trovato questa libreria: [dictdiffer] (https: // github.com/inveniosoftware/dictdiffer) che potrebbe già implementarlo. Su Hacker News c'è una discussione a riguardo dove raymondh mostra come farlo con dizionari non nidificati [in puro Python] (https://news.ycombinator.com/item?id=5771696). –

risposta

3

non ho il tempo di provare questo ora (forse un po 'più tardi), ma qui ci sono due osservazioni:

indici combinati

Se si usa tuple gli indici, come ad esempio questo dict[(5,3,2)] non avresti questo problema. Se basi il tuo dettato di base o anche i dit differenziali su questo, puoi eludere il problema.

Forse potresti anche scrivere alcune classi che riscrivono dict[a][b][c] in dict[(a,b,c)] per rendere trasparente questo cambiamento interno.

di base globale

Non capisco il motivo per cui si utilizza una base globale. Dal mio punto di vista questo rende il codice più complicato senza aggiungere nulla. Perché non solo memorizzare la base come in:

def MyDict(collections.abc.MutableSequence): 
    def __init__(self, base): 
     self._base = base 

my_global_base = dict() 
d = MyDict(my_global_base) 

d[2] = 'abc' # modifies self._base inside of the instance too, because it is the 
      # same object 

Se si desidera modificare tutto il contenuto della base, basta eliminare tutti gli elementi che utilizzano popitem() e poi aggiungerne di nuove usando update(). In questo modo il tuo codice è più flessibile e non ha comportamenti sorprendenti a causa delle variabili globali.

classi base astratte

Quando reimplementare classi come sequenze, dizionari ecc potrebbe venire utile per utilizzare il abstract base classes fornita da Python, che fanno parte del lavoro di implementazione per voi.

+0

Grazie, Georg. Per quanto riguarda la combinazione delle chiavi in ​​una tupla, l'ho preso in considerazione, ma ciò si traduce in una lunga sequenza di tasti per qualsiasi elemento memorizzato nel dizionario, il che sembra inefficiente per il resto. allo scopo originale di ridurre l'impronta di memoria. –

+0

Per quanto riguarda la base globale, lo scopo di questo è che è condiviso da tutte le istanze di DifferentialDict. Dal tuo suggerimento capisco che un riferimento all'oggetto base è passato all'istanza di un nuovo oggetto MyDict, piuttosto che una copia locale all'interno dell'oggetto MyDict. È corretto? (sì, la mia conoscenza/comprensione di ciò è incompleta). –

+0

E darò un'occhiata alle classi base astratte, grazie per il suggerimento. –