J'apprends à utiliser les cornichons. J'ai créé un objet namedtuple, je l'ai ajouté à une liste et j'ai essayé de la gérer. 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 je courais le code sans l'envelopper dans une fonction, cela fonctionnait parfaitement. Une étape supplémentaire est-elle requise pour décaper un objet lorsqu'il est enveloppé dans 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()
Créez le tuple nommé outside 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
peut le trouver; c'est un module global maintenant. Lors de la suppression, 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 n'est pas introspectable ni importable.
Il est important de se rappeler que namedtuple()
est une fabrique de classe; vous lui donnez des paramètres et il retourne un objet de classe à partir duquel vous pouvez créer des instances. pickle
ne stocke que les éléments data contenus dans les instances, ainsi qu'une référence à la chaîne à la classe d'origine pour reconstruire les instances.
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 possibilité de créer une variable namedtuple
créée dynamiquement. Cela est nécessaire dans mon cas car je détermine ses champs uniquement au moment de l'exécution (après une requête de base de données).
Tout ce que je fais est monkey patch the namedtuple
en le déplaçant efficacement vers le module __main__
:
def _CreateNamedOnMain(*args):
import __main__
namedtupleClass = collections.namedtuple(*args)
setattr(__main__, namedtupleClass.__name__, namedtupleClass)
namedtupleClass.__module__ = "__main__"
return namedtupleClass
Notez que le nom namedtuple
(fourni par args
) pourrait écraser un autre membre dans __main__
si vous ne faites pas attention.
J'ai trouvé cette réponse dans un autre fil. Il s’agit de nommer le 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
Vous pouvez également 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")
Le problème ici est que les processus enfants ne sont pas en mesure d'importer la classe de l'objet - dans ce cas, la classe P-, dans le cas d'un projet multimodèle, la classe P doit pouvoir être importée partout où le processus enfant est utilisé.
une solution rapide consiste à le rendre importable en l’affectant à globals ()
globals()["P"] = P