web-dev-qa-db-fra.com

Comment décaper correctement une instance namedtuple

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()
41
Dirty Penguin

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.

63
Martijn Pieters

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.

8
Chuim

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

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")
3
Peque

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
0
rachid el kedmiri