Comment décaper une instance namedtuple correctement
J'apprends à utiliser pickle. J'ai créé un objet namedtuple, l'ai ajouté à une liste et j'ai essayé de décaper cette liste. Cependant, j'obtiens l'erreur suivante:
pickle.PicklingError: Can't pickle <class '__main__.P'>: it's not found as __main__.P
J'ai trouvé que si j'exécutais le code sans l'envelopper dans une fonction, cela fonctionnait parfaitement. Est-il une étape supplémentaire nécessaire pour mariner un objet enveloppé à l'intérieur d'une fonction?
Voici mon code:
from collections import namedtuple
import pickle
def pickle_test():
P = namedtuple("P", "one two three four")
my_list = []
abe = P("abraham", "lincoln", "vampire", "hunter")
my_list.append(abe)
f = open('abe.pickle', 'w')
pickle.dump(abe, f)
f.close()
pickle_test()
5 réponses
Créez le tuple nommé en dehors de de la fonction:
from collections import namedtuple
import pickle
P = namedtuple("P", "one two three four")
def pickle_test():
my_list = []
abe = P("abraham", "lincoln", "vampire", "hunter")
my_list.append(abe)
f = open('abe.pickle', 'w')
pickle.dump(abe, f)
f.close()
pickle_test()
Maintenant pickle
pouvez le trouver; c'est un module mondial maintenant. Lors du découplage, tout ce que le module pickle
A à faire est de localiser à nouveau __main__.P
. Dans votre version, P
est un local, à la fonction pickle_test()
, et ce n'est pas introspectable ou importable.
Il est important de se rappeler que namedtuple()
est une fabrique de classes; vous lui donnez des paramètres et il renvoie un objet de classe à partir duquel vous pouvez créer des instances. pickle
ne stocke que les données contenues dans les instances, plus une référence de chaîne à la classe d'origine pour reconstruire les instances à nouveau.
Après avoir ajouté ma question en tant que commentaire à la réponse principale, j'ai trouvé un moyen de résoudre le problème de la création dynamique d'un cornichon namedtuple
. Ceci est nécessaire dans mon cas car je découvre les champs pendant l'exécution (après une requête DB).
Tout ce que je fais est monkey patch le namedtuple
en le déplaçant vers la __main__
module:
def _CreateNamedOnMain(*args):
import __main__
namedtupleClass = collections.namedtuple(*args)
setattr(__main__, namedtupleClass.__name__, namedtupleClass)
namedtupleClass.__module__ = "__main__"
return namedtupleClass
En sachant que le nom namedtuple
(qui est fourni par args
) pourrait écraser un autre membre dans __main__
Si vous n'êtes pas prudent.
Alternativement, vous pouvez utiliser cloudpickle
ou dill
pour la sérialisation:
from collections import namedtuple
import cloudpickle
import dill
def dill_test(dynamic_names):
P = namedtuple('P', dynamic_names)
my_list = []
abe = P("abraham", "lincoln", "vampire", "hunter")
my_list.append(abe)
with open('deleteme.cloudpickle', 'wb') as f:
cloudpickle.dump(abe, f)
with open('deleteme.dill', 'wb') as f:
dill.dump(abe, f)
dill_test("one two three four")
J'ai trouvé cette réponse dans un autre thread. C'est tout au sujet de la dénomination du tuple nommé. Cela a fonctionné pour moi:
group_t = namedtuple('group_t', 'field1, field2') # this will work
mismatched_group_t = namedtuple('group_t', 'field1, field2') # this will throw the error
Le problème ici est que les processus enfants ne sont pas capables d'importer la classe de l'objet-dans ce cas, la classe P-, dans le cas d'un projet multi-modèle, la classe P devrait être importable partout où le processus enfant est utilisé
Une solution de contournement rapide consiste à le rendre importable en l'affectant à globals ()
globals()["P"] = P