it-swarm.com.de

Wie man eine namedtuple-Instanz korrekt pickle

Ich lerne, wie man Gurke benutzt. Ich habe ein namedtuple-Objekt erstellt, an eine Liste angehängt und versucht, diese Liste auszuwählen. Ich erhalte jedoch die folgende Fehlermeldung:

pickle.PicklingError: Can't pickle <class '__main__.P'>: it's not found as __main__.P

Ich habe festgestellt, dass der Code einwandfrei funktioniert, wenn ich den Code ausgeführt habe, ohne ihn in eine Funktion zu packen. Ist ein zusätzlicher Schritt erforderlich, um ein Objekt in einer Funktion einzuwickeln?

Hier ist mein 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

Erstellen Sie das benannte Tuple outside der Funktion:

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

Jetzt kann pickle es finden; Es ist jetzt ein globales Modul. Nach dem Abnehmen muss das Modul pickle nur noch __main__.P suchen. In Ihrer Version ist P eine local der pickle_test()-Funktion, und diese Funktion ist nicht introspektierbar oder importierbar.

Es ist wichtig zu wissen, dass namedtuple() eine Klassenfabrik ist. Sie geben ihm Parameter und es gibt ein Klassenobjekt zurück, aus dem Sie Instanzen erstellen können. pickle speichert nur die in den Instanzen enthaltenen data und einen String-Verweis auf die ursprüngliche Klasse, um die Instanzen erneut zu rekonstruieren.

63
Martijn Pieters

Nachdem ich meine Frage als Kommentar zur Hauptantwort hinzugefügt hatte, fand ich einen Weg, das Problem zu lösen, eine dynamisch erstellte namedtuple pickle-fähig zu machen. Dies ist in meinem Fall erforderlich, da ich die Felder nur zur Laufzeit (nach einer DB-Abfrage) ermittle.

Alles was ich mache, ist monkey patch die namedtuple, indem ich es effektiv in das __main__-Modul schiebe:

def _CreateNamedOnMain(*args):
    import __main__
    namedtupleClass = collections.namedtuple(*args)
    setattr(__main__, namedtupleClass.__name__, namedtupleClass)
    namedtupleClass.__module__ = "__main__"
    return namedtupleClass

Beachten Sie, dass der namedtuple-Name (der von args bereitgestellt wird) ein anderes Mitglied in __main__ überschreiben kann, wenn Sie nicht aufpassen.

8
Chuim

Ich habe diese Antwort in einem anderen Thread gefunden. Hier geht es um die Benennung des genannten Tupels. Das hat für mich funktioniert:

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

Alternativ können Sie cloudpickle oder dill zur Serialisierung verwenden:

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

Das Problem hierbei ist, dass die untergeordneten Prozesse die Klasse des Objekts nicht importieren können - in diesem Fall die Klasse P -. Im Falle eines Projekts mit mehreren Modellen sollte die Klasse P überall dort importiert werden können, wo der untergeordnete Prozess verwendet wird

eine schnelle Problemumgehung besteht darin, es importierbar zu machen, indem es auf globals () angewendet wird.

globals()["P"] = P
0