web-dev-qa-db-fra.com

Can't pickle defaultdict

J'ai un défaut par défaut qui ressemble à ceci:

dict1 = defaultdict(lambda: defaultdict(int))

Le problème est que je ne peux pas le décaper avec cPickle. L'une des solutions que j'ai trouvées ici consiste à utiliser une fonction de niveau module au lieu d'un lambda. Ma question est, quelle est la fonction au niveau du module? Comment puis-je utiliser le dictionnaire avec cPickle?

49
Fynn Mahoney

En plus de explication de Martijn :

Une fonction au niveau du module est une fonction qui est définie au niveau du module, ce qui signifie que ce n'est pas une méthode d'instance d'une classe, elle n'est pas imbriquée dans une autre fonction, et c'est une fonction "réelle" avec un nom, pas une fonction lambda .

Donc, pour décaper votre defaultdict, créez-le avec une fonction de niveau module au lieu d'une fonction lambda:

def dd():
    return defaultdict(int)

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

que vous pouvez le mariner

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

Pickle souhaite stocker tous les attributs d'instance et les instances defaultdict stockent une référence à l'appelable default. Pickle revient sur chaque attribut d'instance.

Le cornichon ne peut pas manipuler les lambdas; pickle ne gère que les données, pas le code, et les lambdas contiennent du code. Les fonctions peuvent être décapées, mais tout comme les définitions de classe uniquement si la fonction peut être importée. Une fonction définie au niveau du module peut être importée. Pickle stocke juste une chaîne dans ce cas, le "chemin" complet de la fonction à importer et référencé lors du nouveau décrochement.

16
Martijn Pieters

Vous pouvez cependant utiliser partial pour accomplir ceci:

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

Pour ce faire, écrivez simplement le code que vous vouliez écrire. J'utiliserais aneth , qui peut sérialiser les lambdas et les defaultdicts. Dill peut sérialiser presque n'importe quoi en 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>, {})
7
Mike McKerns

Si vous ne vous souciez pas de conserver le type defaultdict, convertissez-le:

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

Je pense que c'est une excellente alternative car lorsque vous décapez, l'objet est probablement dans sa forme finale ... ET, si vous avez vraiment besoin à nouveau du type defaultdict, vous pouvez simplement convertir est de retour après avoir décroché:

for value in my_dict:
    my_dict[value] = defaultdict(type, my_dict[value])
nested_default_dict = defaultdict(type, my_dict)
2
user2921352
dict1 = defaultdict(lambda: defaultdict(int))
cPickle.dump(dict(dict1), file_handle)

travaillé pour moi

2
Avi

L'implémentation de la fonction lambda anonyme par une fonction normale a fonctionné pour moi. Comme l'a souligné Mike, Pickle ne peut pas gérer les lambdas; pickle ne gère que les données. Par conséquent, la conversion de la méthode defaultdict à partir de:

    dict_ = defaultdict(lambda: default_value)

à:

    def default_():
        return default_value

puis la création du dict par défaut comme suit a fonctionné pour moi:

    dict_ = defaultdict(default_)
1
Addishiwot Shimels

Je fais actuellement quelque chose de similaire à la question poser, cependant, j'utilise une sous-classe de defaultdict qui a une fonction membre qui est utilisée comme default_factory. Afin que mon code fonctionne correctement (j'avais besoin que la fonction soit définie au moment de l'exécution), j'ai simplement ajouté du code pour préparer l'objet au décapage.

Au lieu de:

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

J'utilise ceci:

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

Ce n'est pas le code exact que j'ai utilisé car mon arbre est un objet qui crée des instances du même type d'arbre que les index sont demandés (donc j'utilise une fonction membre récursive pour faire les opérations de pré/post pickle), mais ce modèle aussi répond à la question.

1
Sandy Chapman