2013-05-08 5 views
34

Ich habe eine defaultdict Beize, die wie folgt aussieht:Kann nicht defaultdict

dict1 = defaultdict(lambda: defaultdict(int)) 

Das Problem ist, ich kann nicht beizen es cPickle verwenden. Eine der Lösungen, die ich hier gefunden habe, ist die Verwendung von Modulebenen anstelle von Lambda. Meine Frage ist, was ist Modul-Level-Funktion? Wie kann ich das Wörterbuch mit cPickle verwenden?

Antwort

40

Neben Martijn's explanation:

A-Modul-Level-Funktion ist eine Funktion, die auf Modulebene definiert ist, daß sie nicht ein ist, bedeutet, Instanz Methode einer Klasse, es ist nicht verschachtelt in einer anderen Funktion, und es ist eine "echte" Funktion mit einem Namen, keine Lambda-Funktion.

Also, um Ihre defaultdict Beize, erstellen Sie es mit dem Modul-Level-Funktion anstelle einer Lambda-Funktion:

def dd(): 
    return defaultdict(int) 

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

als Sie können es Pickles

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

Pickle möchte alle Instanzattribute speichern, und defaultdict Instanzen speichern einen Verweis auf die default aufrufbar. Pickle rekursiv über jedes Instanzattribut.

Pickle kann nicht mit Lambdas umgehen; pickle behandelt nur Daten, nicht Code, und lambdas enthalten Code. Funktionen können eingelegt werden, aber genauso wie Klassendefinitionen nur, wenn die Funktion importiert werden kann. Eine auf Modulebene definierte Funktion kann importiert werden. Pickle speichert in diesem Fall nur einen String, den vollständigen 'Pfad' der zu importierenden Funktion, auf den beim erneuten Entspinnen verwiesen wird.

7

Sie können jedoch partial verwenden, um dies zu erreichen:

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

Könnten Sie für mich entpacken, wie das funktioniert? Ich bin fasziniert ... – Fred

1

ich zur Zeit etwas Ähnliches tue Zu der Frage poser verwende ich jedoch eine Unterklasse von defaultdict, die eine Mitgliedsfunktion hat, die als default_factory verwendet wird. Damit mein Code ordnungsgemäß funktioniert (ich musste die Funktion zur Laufzeit definieren), fügte ich einfach einen Code hinzu, um das Objekt für das Beizen vorzubereiten.

Statt:

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

Ich benutze diese:

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

Dies ist nicht der genaue Code, den ich als mein Baum verwendet wird, ist ein Objekt, das Instanzen derselben des Baumes Typ erzeugt als Indizes werden angefordert (daher verwende ich eine rekursive Elementfunktion, um die Vor-/Nachbeizoperationen durchzuführen), aber dieses Muster beantwortet auch die Frage.

+0

Beachten Sie, dass dies nur gut ist, wenn Sie nicht die default_factory des eingelegten Diktats verlieren wollen. Wenn du die Fabrik nicht mehr benötigst, kannst du sie einfach auf "None" setzen und fertig sein (: – drevicko

5

Um dies zu tun, schreiben Sie einfach den Code, den Sie schreiben wollten. Ich würde dill verwenden, die lambdas und defaultdicts serialisieren kann. Dill kann fast alles in Python serialisieren.

>>> 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

Das funktioniert gut. Gibt es eine Möglichkeit, dict1 in einer temporären Datei zu speichern und dann wieder zu laden? Etwas ähnliches zum Pickle-Betrieb des Schreibens und des Lesens von Dateien .. –

+0

Sure. 'dill' bietet die üblichen' dump' und 'load', die genau wie' dump' und 'load' von' pickle' verwendet werden können Ich möchte 'dill.temp.dump' auschecken, was zu einer' NamedTemporaryFile' führt. –

+0

Danke, schau dir die letzte Frage in meinem Profil an. Du könntest deine Antwort dort posten. :) –

1

Wenn Sie sich über die Erhaltung der defaultdict Typ sich nicht, wandeln es:

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 

Ich denke, das da eine gute Alternative, wenn Sie Beizen sind, ist das Objekt wahrscheinlich in seiner endgültigen Form ist ... UND, wenn Sie den defaultdict-Typ wirklich noch einmal benötigen, können Sie ihn einfach nach dem Auspinnen wiederherstellen:

Verwandte Themen