2013-05-08 15 views
34

Ho un defaultdict che assomiglia a questo:Impossibile salamoia defaultdict

dict1 = defaultdict(lambda: defaultdict(int)) 

Il problema è che non riesco a Pickle utilizzando cPickle. Una delle soluzioni che ho trovato qui è quella di usare la funzione a livello di modulo invece di una lambda. La mia domanda è, qual è la funzione a livello di modulo? Come posso usare il dizionario con cPickle?

risposta

40

Oltre a Martijn's explanation:

Una funzione a livello di modulo è una funzione che viene definito a livello di modulo, che significa che non è un metodo di istanza di una classe, non è annidato in un'altra funzione, ed è una funzione "reale" con un nome, non una funzione lambda.

Quindi, fare la serializzazione vostro defaultdict, crearla con la funzione a livello di modulo invece di una funzione lambda:

def dd(): 
    return defaultdict(int) 

dict1 = defaultdict(dd) # dd is a module-level function 

di quanto si può salamoia che

tmp = pickle.dumps(dict1) # no exception 
new = pickle.loads(tmp) 
11

Pickle desidera memorizzare tutti gli attributi di istanza e le istanze defaultdict memorizzano un riferimento al numero civico default. Pickle ricorre su ogni attributo di istanza.

Pickle non può gestire lambda; pickle gestisce solo i dati, non il codice e lambda contiene il codice. Le funzioni possono essere decodificate, ma solo come le definizioni di classe solo se la funzione può essere importata. Una funzione definita a livello di modulo può essere importata. Pickle memorizza semplicemente una stringa in quel caso, il "percorso" completo della funzione da importare e fare riferimento quando si rimuove nuovamente.

7

È tuttavia possibile utilizzare partial per raggiungere questo:

>>> from collections import defaultdict 
>>> from functools import partial 
>>> pickle.loads(pickle.dumps(defaultdict(partial(defaultdict, int)))) 
defaultdict(<functools.partial object at 0x94dd16c>, {}) 
+1

Potresti disimballare per me come funziona? Sono incuriosito ... – Fred

1

Attualmente sto facendo qualcosa di simile alla domanda poser, tuttavia, sto usando una sottoclasse di defaultdict che ha una funzione membro che viene usata come default_factory. Per far funzionare correttamente il mio codice (ho richiesto che la funzione fosse definita in fase di runtime), ho semplicemente aggiunto del codice per preparare l'oggetto al decapaggio.

Invece di:

... 
pickle.dump(dict, file) 
... 

Io uso questo:

.... 
factory = dict.default_factory 
dict.default_factory = None 
pickle.dump(dict, file) 
dict.default_factory = factory 
... 

Questo non è il codice esatto che ho usato come mio albero è un oggetto che crea istanze dello stesso tipo del albero come gli indici sono richiesti (quindi uso una funzione membro ricorsiva per eseguire le operazioni pre/post pickle), ma questo modello risponde anche alla domanda.

+0

Si noti che questo è buono solo se non si cura di perdere il valore di default_factory del decritato. Se non hai più bisogno della fabbrica, puoi semplicemente impostarla su "Nessuna" e fare (: – drevicko

5

Per fare ciò, è sufficiente scrivere il codice che si desidera scrivere. Vorrei usare dill, che può serializzare lambdas e defaultdicts. Dill può serializzare quasi qualsiasi cosa in python.

>>> import dill 
>>> from collections import defaultdict 
>>> 
>>> dict1 = defaultdict(lambda: defaultdict(int)) 
>>> pdict1 = dill.dumps(dict1) 
>>> _dict1 = dill.loads(pdict1) 
>>> _dict1 
defaultdict(<function <lambda> at 0x10b31b398>, {}) 
+0

Funziona bene. C'è un modo per scaricare dict1 in un file temporaneo e poi ricaricarlo di nuovo? per l'operazione pickle di scrittura e lettura da file. –

+0

Certo. 'dill' fornisce i soliti' dump' e 'load' che possono essere usati come' dump' e 'load' da' pickle'. voglio dare un'occhiata a 'dill.temp.dump' che scarica un' NamedTemporaryFile'. –

+0

Grazie, controlla l'ultima domanda sul mio profilo. Potresti pubblicare la tua risposta lì :) –

1

Se non si preoccupano di conservare il tipo defaultdict, convertirlo:

fname = "file.pkl" 

for value in nested_default_dict: 
    nested_default_dict[value] = dict(nested_default_dict[value]) 
my_dict = dict(nested_default_dict) 

with open(fname, "wb") as f: 
    pickle.dump(my_dict, f) # Now this will work 

Penso che questa sia una grande alternativa dal momento che quando si è decapaggio, l'oggetto è probabilmente nella sua forma definitiva ... E, se davvero hai ancora bisogno del tipo defaultdict, puoi semplicemente riconvertire dopo aver annullato:

for value in my_dict: 
    my_dict[value] = defaultdict(type, my_dict[value]) 
nested_default_dict = defaultdict(type, my_dict) 
Problemi correlati