2013-05-08 11 views
34

Mam defaultdict, który wygląda tak:Nie można marynowane defaultdict

dict1 = defaultdict(lambda: defaultdict(int)) 

Problem polega na tym, że nie można go za pomocą cPickle marynowane. Jednym z rozwiązań, które tutaj znalazłem, jest użycie funkcji poziomu modułu zamiast lambda. Moje pytanie brzmi, co to jest funkcja na poziomie modułu? Jak mogę używać słownika z cPickle?

Odpowiedz

40

Oprócz Martijn's explanation:

Funkcja tym module jest funkcją, która jest określona na poziomie modułu, co oznacza, że ​​nie jest to Metoda instancji klasy, nie jest zagnieżdżona w innej funkcji i jest "prawdziwą" funkcją o nazwie, a nie funkcją lambda.

Tak więc, aby marynowane swojej defaultdict, utwórz go z funkcji modułu szczebla zamiast funkcji lambda:

def dd(): 
    return defaultdict(int) 

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

niż można marynowane to

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

Pickle chce przechowywać wszystkie atrybuty instancji, a instancje defaultdict przechowują odwołanie do podpytania default. Pickle recurses nad każdym atrybutem instancji.

Marynata nie może obsłużyć lambd; pickle tylko kiedykolwiek obsługuje dane, nie kod, a lambda zawiera kod. Funkcje można korygować w postaci, ale podobnie jak definicje klas, tylko jeśli funkcja może być zaimportowana . Funkcję zdefiniowaną na poziomie modułu można zaimportować. Pickle po prostu zapisuje ciąg znaków w tym przypadku, pełną "ścieżkę" funkcji, która ma zostać zaimportowana i odniesiona, gdy zostanie ponownie rozpakowany.

7

Można jednak użyć partial do osiągnięcia tego celu:

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

Czy możesz rozpakować dla mnie, jak to działa? Jestem zaintrygowany ... – Fred

1

Jestem obecnie robi coś podobnego do zapytania poser, jednak używam podklasy defaultdict, która ma funkcję składową, która jest używana jako default_factory. Aby mój kod działał poprawnie (wymagałem zdefiniowania funkcji w czasie wykonywania), dodałem po prostu kod do przygotowania obiektu do wytrawiania.

Zamiast:

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

Używam tego:

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

To nie jest dokładny kod użyłem jako mojego drzewa jest obiektem, który tworzy instancje tego samego typu drzewka jako żądane są indeksy (dlatego używam rekurencyjnej funkcji członkowskiej do wykonywania operacji pikle przed/po), ale ten wzór również odpowiada na pytanie.

+0

Zauważ, że jest to dobre tylko wtedy, gdy nie chcesz stracić 'default_factory' marynowanego dict. Jeśli nie potrzebujesz już fabryki, możesz po prostu ustawić ją na "Brak" i zrobić (: – drevicko

5

Aby to zrobić, po prostu napisz kod, który chcesz napisać. Użyłbym dill, który może serializować lambda i defaultdicts. Dill może serializować prawie wszystko w pythonie.

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

To działa dobrze. Czy istnieje sposób na zrzucenie dict1 w pliku tymczasowym, a następnie załadowanie go z powrotem? Coś podobnego do piklowania operacji zapisu i odczytu z plików .. –

+0

Oczywiście. 'dill' zapewnia typowe' dump' i 'load', które mogą być używane tak jak' dump' i 'load' z' pickle'. chcesz sprawdzić 'dill.temp.dump', który zrzuca do' NamedTemporaryFile'. –

+0

Dzięki, sprawdź najnowsze pytanie na moim profilu. Możesz tam umieścić odpowiedź. :) –

1

Jeśli nie dbają o zachowanie typu defaultdict, konwertować go:

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 

myślę, że jest to świetna alternatywa od kiedy jesteś marynowanie, obiekt jest prawdopodobnie w jego ostatecznej postaci ... ORAZ, jeśli naprawdę potrzebujesz ponownie tego typu defaultdict, możesz po prostu przekonwertować ponownie po odkryciu:

for value in my_dict: 
    my_dict[value] = defaultdict(type, my_dict[value]) 
nested_default_dict = defaultdict(type, my_dict) 
Powiązane problemy